用Python类开发

时间:2018-04-17 18:53:14

标签: python

没有什么比Python Class概念更令我害怕;最近我一直在尝试使用/创建类来了解它们的用途,结构和功能等。但是,我不清楚类的概念以及如何创建它们。

请查看以下示例:

class Prob(object):
    def __init__ (self,filename):
        self.file_contents = read_file(filename)
    def prob_build(self):
        self.problem, self.aux_vars = build_problem(file_contents)

first_object = Prob(some_file)
alpha,beta, gamma = first_object.prob_build() 

在这种情况下,read_filebuild_problem是自定义函数,它们分别从CSV文件读取数据并构建基于PuLP的线性问题。现在,我的理解是,当我基于Prob类初始化对象时,file_contents可供类在内部使用。在这种情况下,如何获取变量alphabetagamma?对于当前代码,我得到一个TypeError: 'NoneType' object is not iterable.我已经测试了函数,并且我知道它们没有任何错误地工作,这让我觉得我定义类的方式有问题。

有什么建议吗?

4 个答案:

答案 0 :(得分:1)

您的prob_build方法需要return三个值。您当前没有明确地return任何内容,因此它隐式返回单个值None,并且Python尝试将其解析为三个值,并且显然失败。

这与这是一个类的方法无关;函数返回值,无论它们的定义方式和位置如何。

当然,功能不能返回任何东西是完全可以的;但显然它没有产生你可以用variable = func()

之类的东西获得的结果

答案 1 :(得分:1)

  

没有什么比Python类概念更让我害怕了;

这实际上不是Python概念 - 大多数面向对象语言中都存在类。

  

最近我一直在尝试(...)了解他们的目的,结构和功能等。但是,我不清楚类的概念

在我们讨论课程之前,你必须了解对象。对象是将状态(一组数据)和行为(一组作用于状态或根据状态的功能组合在一起的方式组合在一起的方法。现在这是一个抽象的定义,让我们看看它是如何工作的一个简单的例子 - 二维空间中的几何点。

对于状态部分,2d点由其x和y坐标定义。您可以使用dict

来表示这一点
my_point = {"x": 0, "y": 0}

好的,很好,但不是很明确,有点容易出错。我们可以从一个负责创建新点的函数开始:

def new_point(x=0, y=0):
    return {"x": x, "y": y}

p1 = new_point()
p2 = new_point(42, 84)

现在我们可以建立积分而不必担心血腥细节。好的,现在让我们做一些行为......第一个有用的功能是检查两个点是否相等(假设它们具有相同的坐标,它们是相等的):

def points_are_equal(p1, p2):
    return p1["x"] == p2["x"] and p1["y"] == p2["y"]

您可以看到此行为取决于两个点状态。

我们还可能想要沿水平轴移动一个点:

def move_x(p, distance):
    p["x"] += distance

或沿垂直轴:

def move_y(p, distance):
    p["y"] += distance

或两者同时:

def move_by(p, x_distance, y_distance):
    move_x(p, x_distance)
    move_y(p, y_distance)           

请注意,这里的行为是改变点的状态。

当然,我们希望有办法获得点的x或y坐标:

 def get_x(p):
     return p["x"]

 def get_y(p)
     return p["y"]

我们在这里构建的是所谓的“抽象数据类型”:不是手动构建一个dict,手动比较两个dicts,手动更新我们的dict并手动检查它的状态,我们已经定义了一个函数集做所有这些,或多或少隐藏内部表示。

  

以及如何创建它们。

大多数情况下,课程是另一种做同样事情的方式,但还有很多其他方面的好处。让我们将我们的“point”数据类型重写为Python类:

class Point(object):
    # this is the function that creates a new point
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    # equality test:
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    # move
    def move_x(self, distance):
        self.x += distance

    def move_y(self, distance):
        self.y += distance

    def move_by(self, x_distance, y_distance):
        self.move_x(x_distance)
        self.move_y(y_distance)

我们实际上不需要写get_x()也不需要get_y(),我们可以直接访问x和y:

p = Point(2, 5)
print(p.x)
print(p.y)

p.move_by(3, 1)
print(p.x)
print(p.y)

p2 = Point(p.x, p.y)
print(p == p2) # => True
p2.move_x(3)
print(p == p2) # => False

实际上,在幕后,我们的p对象一个词典:

print(p.__dict__)

其他OOPL可能会使用其他方式来存储对象的状态(例如,类似C语言的结构),但在Python中,对象实际上主要是一个字典。嗯,dict加上一个类:

print(p.__class__)

和一组“属性查找规则”(由基类object提供),它将首先在对象的__dict__上查找属性然后在对象的类上查找属性(这是{{1}的方式}实际上被解释为p.move_x(42)

类和对象提供了许多其他好东西(继承等),但基本上它们只是这样:一个dict(存储状态)和一个类(存储行为)。

现在举个例子:

  

我的理解是,当我基于我的Prob类初始化对象时,file_contents可供类在内部使用

Point.move_x(p, 42)可用于实例 - 类函数可以在当前实例上访问 - 这是file_contents参数。我的self函数应该使用build_prob

self.file_contents

然后,您可以访问您实例上的def prob_build(self): self.problem, self.aux_vars = build_problem(self.file_contents) problem

aux_vars

请注意,first_object = Prob(some_file) first_object.prob_build() print(first_object.problem) print(first_object.aux_vars) problem属性仅在您调用aux_vars后才存在。这被认为是不好的做法,因为您可以获得prob_build

AttributeError

解决这个问题的第一步是在first_object = Prob(some_file) # Doesn't work !!! print(first_object.problem) 方法中初始化这些属性(是的,这就是为什么它被称为“ init ”):

__init__

但这并不是更好 - 您仍需要致电class Prob(object): def __init__ (self,filename): self.file_contents = read_file(filename) self.problem = None self.aux_vars = None def prob_build(self): self.problem, self.aux_vars = build_problem(self.file_contents) 以获得可用的状态。这里明显的解决方法是在初始化程序中执行全部初始化并删除yourobj.prob_build()

prob_build

但是你可以问问自己:如果没有这种行为,这个课程的重点是什么,你所做的就是:

class Prob(object):
    def __init__ (self,filename):
        self.file_contents = read_file(filename)
        self.problem, self.aux_vars = build_problem(self.file_contents)

你也可以用一个简单的函数替换它:

 prob = Prob("path/to/file.csv")
 prob, aux_vars = prob.problem, prob.aux_vars
 result = do_something_with(prob, aux_vars)

作为一般规则,如果你的班级没有状态或没有行为,你很可能不需要一个班级。当然,这条规则有例外,但这仍然是一个很好的指导方针。在您的情况下,假设的def build_problem_from_file(path): return build_problem(read_file(path)) prob, aux_vars = build_problem_from_file(...) result = do_something_with(prob, aux_vars) 也可能是一种方法:

do_something_with(prob, aux_vars)

但如果这是唯一的行为,你仍然不需要一个类:

class Prob(object):
    def __init__ (self,filename):
        self.file_contents = read_file(filename)
        self.problem, self.aux_vars = build_problem(self.file_contents)

     def do_something(self):
         # some computations here using self.problem and self.aux_vars
         return result  

 prob = Prob("path/to/file.csv")
 result = prob.do_something()

所以长话短说:问问自己是否以及为什么要上课。对于某些问题,OOP是一个很好的解决方案,但不是所有问题的解决方案。

答案 2 :(得分:0)

与普通functions类似,如果您想要结果,班级methods需要返回一些内容!在您的代码中,prob_build不会返回任何内容!所以,它应该是这样的:

def prob_build(self):
    self.problem, self.aux_vars = build_problem(file_contents)
    return (self.problem, self.aux_vars)

请注意,在这种情况下,它返回两个参数。您应该更加明确alphabetagamma的内容!

答案 3 :(得分:0)

您的类函数prob_build()不返回任何内容。它只设置类内部的变量。