存在基于初始属性在 init 方法之外创建对象属性的不同方法



我试图理解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__并尝试找到属性x1x2,所以如果它在那里找不到它们,那么它会转到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的解决方案非常有趣,实际上它甚至可以让您使用缓存(当然,如果我们编写!(,因此您不需要计算您的属性lengthslopey_intercept每次调用这些变量时,您才能计算它们的值,只有当您更改属性的值时points

希望对您有所帮助,再见!

Don't use "improvisation", use Python.

最新更新