没有什么比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_file
,build_problem
是自定义函数,它们分别从CSV文件读取数据并构建基于PuLP的线性问题。现在,我的理解是,当我基于Prob
类初始化对象时,file_contents可供类在内部使用。在这种情况下,如何获取变量alpha
,beta
和gamma
?对于当前代码,我得到一个TypeError: 'NoneType' object is not iterable.
我已经测试了函数,并且我知道它们没有任何错误地工作,这让我觉得我定义类的方式有问题。
有什么建议吗?
答案 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)
请注意,在这种情况下,它返回两个参数。您应该更加明确alpha
,beta
和gamma
的内容!
答案 3 :(得分:0)
您的类函数prob_build()不返回任何内容。它只设置类内部的变量。