我试图理解OOP(特别是在Python 3中(。下面是一个基本的类模板:
class Lines:
"""
Arguments: list of coordinates
"""
def __init__(self, points):
self.x1 = points[0]
self.y1 = points[1]
self.x2 = points[2]
self.y2 = points[3]
我将列表中的开始和结束 (x,y( 坐标传递给类。 但是,我还想为此类Lines
的对象添加长度、斜率和y 截距属性(注意:我不希望它们作为方法(。以下是我发现的几种方法-
添加到 init 方法本身
class Lines:
def __init__(self, points):
self.x1 = points[0]
self.y1 = points[1]
self.x2 = points[2]
self.y2 = points[3]
##Create length, slope, and y_intercept attributes
self.length = round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2)
self.slope = round((self.y2 - self.y1)/(self.x2-self.x1),1)
self.y_intercept = self.y1 - self.slope*self.x1
为它们创建方法并使用@property装饰器
class Lines:
def __init__(self, points):
self.x1 = points[0]
self.y1 = points[1]
self.x2 = points[2]
self.y2 = points[3]
@property
def length(self):
return round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2)
@property
def slope(self):
return round((self.y2 - self.y1)/(self.x2-self.x1),1)
@property
def y_intercept(self):
return self.y1 - self.slope*self.x1
我的问题:
我不喜欢第一种方法(使用init()
(,因为在那里安装这样的基于计算的代码看起来有点麻烦。我的理解是,init 方法只是用来初始化对象的属性,而不是更多。
我可以使用属性装饰器,然后像这样访问对象的斜坡等
line1 = Lines([0,0,2,4])
line1.slope
但是,当我打印line1.__dict__
时,它们未在可用属性中列出。
我的问题
我真正想知道的是,是否有其他(更常用的Pythonic(方法可以根据初始属性(例如:x1,y1(为对象设置属性(例如:斜率(。我认为这将是一个非常普遍的问题(即,将一组基本属性作为输入,然后基于它们将其他更高级的属性设置为同一对象(,但我在这里没有找到太多。我确定我在这里错过了一些简单而优雅的解决方案。
提前谢谢你。我希望我的问题很清楚。
您可以通过几种方式做到这一点,也许它们并不"常见",但它们仍然Pythonic
。
一、__dict__
考虑以下简单代码:
class Lines:
pass
line1 = Lines()
现在,我想更改属性xy
,我的意思是将其设置为10
。一旦Python是动态的,并且Python中的所有内容都是一个对象,我就可以做到,看看:
class Lines:
pass
line1 = Lines()
line1.xy = 10
print(f"line1.xy = {line1.xy}") #Output: line1.xy = 10
什么?如何设置不存在的属性?!很简单,__dict__
.它存储通过实例设置的每个属性。
class Lines:
pass
line1 = Lines()
line1.xy = 10
print(f"xy = {line1.xy}") #Output: xy = 10
print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {'xy': 10}
那么,如果__dict__
存储通过实例设置的属性,那么在我设置任何属性之前,__dict__
状态如何?!正如你可以推断的那样,它从空开始。
因此,您应该记住,__dict__
不显示可用的属性,而是显示已设置的属性,并且这些属性是否存在于main class
但是,当我打印
line1.__dict__
时,它们未列在可用的属性中。
事实上,__dict__
中的所有属性都可以通过它获得,但是如果您尝试获取不在__dict__
中但在类中定义的任何其他属性,您可以获得它。如何?看一看:
class Lines:
x1 = 3
x2 = 7
line1 = Lines()
print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {}
print(f"x1 = {line1.x1}") #Output: x1 = 3
print(f"x2 = {line1.x2}") #Output: x2 = 7
print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {}
line1.x1 = 9 #Setting x1, so it will be stored at __dict__
print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {'x1': 9}
print(f"x1 = {line1.x1}") #Output: x1 = 9
print(f"x2 = {line1.x2}") #Output: x2 = 7
print(f"test = {line1.test}") #Output: AttributeError
蟒蛇在这里做什么?!Python首先去__dict__
并尝试找到属性x1
和x2
,所以如果它在那里找不到它们,那么它会转到main class
,如果找到它们,它会为您返回它们,如果不是Python提出AttributeError
。
__dict__
很棒,它使您能够做很多事情。由于任何属性值都存储在__dict__
您可以直接通过它获取其值,请查看:
example = line1.__dict__['x1']
print(f"__dict__['x1'] = {example}") #Output: __dict__['x1'] = 9
您的问题和疑问
关于pythonic代码的争论真的很令人讨厌,但是我们必须问自己这些代码是否是python,我的意思是如果某些代码是python并且没有"improvisation"
,那么它是pythonic的,即使该代码不是那么通用,例如方法getattr()
,setattr()
和delattr()
, 有时它们的使用比"dotted notation"
,因为它们更可靠,尤其是当您深入了解正在做的事情并且正在调试时。(至少我是这么认为的(
所以一切都围绕着你正在做什么和你想做什么。 想象一下,你有一个高质量的代码,你使用不那么常见的函数,你的代码不是Pythonic吗?!为了清楚起见,假设您正在编写一个每次执行事务时都会增加的计数,因此您count += 1
使用常用方式,您超越并使用itertools.count()
,毫无疑问,您拥有高质量的代码,即使通常人们不使用该模块,您也可以在此处查看更多相关信息: itertools — 创建迭代器以实现高效循环的函数。
因此,让我们转到代码:如您所见,这是解决此问题的另一种方法。
class Lines:
def __init__(self, points):
self.x1 = points[0]
self.y1 = points[1]
self.x2 = points[2]
self.y2 = points[3]
self.length = self._length()
self.slope = self._slope()
self.y_intercept = self._y_intercept()
def _length(self):
return round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2)
def _slope(self):
return round((self.y2 - self.y1)/(self.x2-self.x1),1)
def _y_intercept(self):
return self.y1 - self.slope*self.x1
line1 = Lines([0,0,2,4])
print(line1.length)
print(line1.slope)
print(line1.y_intercept)
您使用Read-Only property
的解决方案非常有趣,实际上它甚至可以让您使用缓存(当然,如果我们编写!(,因此您不需要计算您的属性length
,slope
和y_intercept
每次调用这些变量时,您才能计算它们的值,只有当您更改属性的值时points
。
希望对您有所帮助,再见!
Don't use "improvisation", use Python.