我被告知在类范围内声明动态属性不是'Python方式',但我不明白为什么。 有人可以向我解释这个或者给我一些文件,告诉我为什么这是一件坏事吗?老实说,我认为这是一个很好的做法,如果有任何自我记录代码。
示例:
class ClassA(object):
user_data = {}
def set_user(self):
self.user_data['username'] = 'fred'
我可以看到不使用它的唯一原因是属性是静态的(因此可能会产生误导)..
答案 0 :(得分:3)
在评论中,您说,“每个实例都有不同的可变user_data。”不,它不会。 ClassA的每个实例都将共享相同的user_data字典:
>>> class ClassA(object):
... user_data = {}
... def set_user(self, name):
... self.user_data['name'] = name
...
>>> a1 = ClassA()
>>> a1.set_user('fred')
>>> a1.user_data
{'name': 'fred'}
>>>
>>> a2 = ClassA()
>>> a2.user_data
{'name': 'fred'}
>>> a2.set_user('barney')
>>> a2.user_data
{'name': 'barney'}
>>>
>>> a1.user_data
{'name': 'barney'}
>>>
>>> a1.user_data is a2.user_data
True
这不是Pythonic是否存在的问题。这是编写符合您需要的代码的问题。
答案 1 :(得分:1)
使用显示的代码,user_data
不是动态属性(它不是“动态地”在类实例上创建的)。这是一个类属性,它更像是我认为的其他语言中的“静态”属性。这意味着它是在读取和初始化类时在类上声明的属性。这具有能够通过self.whatever
访问同一对象的所有实例的副作用/好处。
换句话说:
class Foo(object):
whatever = {}
def __init__(self):
print self.whatever is Foo.whatever
将始终打印True
。当然,您可以通过向实例添加不同的whatever
属性来更改此行为:
class Bar(object):
whatever = {}
def __init__(self):
self.whatever = {}
print self.whatever is Bar.whatever
使用Foo
,如果我添加项目foo_instance.whatever['foo'] = 'bar'
,则foo_instance2
也会看到此更改,而Bar
则不会出现这种情况。< / p>
答案 2 :(得分:0)
以下是使用类和实例成员的示例:
class Test(object):
instances = {}
def __init__(self, name):
self.instance = self
self.instances[name] = self
# rest of class
a = Test('alfred')
b = Test('banana')
# use a to get a
print a.instance
>>>
<__main__.Test object at 0x0000000002BEBC50>
# get all instances of the Test class
print Test.instances
>>>
{'banana': <__main__.Test object at 0x0000000002BEBDA0>, 'alfred': <__main__.Test object at 0x0000000002BEBC50>}
由于这些仅仅是示例,它们并没有反映任何具体的内容,只是可以做的事情。
在某些情况下声明一个类成员很有用,如上所述,如果你想能够记录,并控制某个类的所有类型。但是你应该总是理解你在做什么,并且不要认为instance
是每个实例都是本地的。
关于'最佳实践'和'最pythonic方式'的问题。就是这样,意见。有些人会不赞同某些事情,而不是其他人。只要您遵守您碰巧参与的任何劳动力规定的标准和标准,您就应该以一种让您感到舒适并且适合您的方式撰写。
答案 3 :(得分:0)
要清楚,类属性可能与您预期的不同。
示例:
class Example(object):
class_data={}
def __init__(self,val):
self.val=val
def __repr__(self):
return str(self.val)
>>> Example.class_data['one']=1 # assign value to the empty dict in class Example
>>> e1,e2=Example(1), Example(2) # INSTANCES-self.val different for each instance
>>> print e1,e2,e1.class_data,e2.class_data
1 2 {'one': 1} {'one': 1}
^^^^^ ^^^^^ # SAME class_data in two different instances
请注意,示例的每个实例都具有相同的class_data可变副本。它不受保护,因为静态类变量可以是Java或C ++。
实际上,您可以随时随意更改,添加或删除这些类属性:
>>> Example.class_data='old dict is gone gone gone...'
>>> print e1,e2,e1.class_data,e2.class_data
1 2 old dict is gone gone gone... old dict is gone gone gone...
你可能会看到这样的代码:
class Example(object):
class_constants=('fee','fie','foe')
# ...
由于这种错误的安慰,它表现得像常数(在另一种语言中):
>>> e1=Example(1)
>>> e1.class_constants[4]='fum'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
但这是一种错误的安慰,因为一段代码总能做到这一点:
>>> Example.class_constants=('another','tuple','of','constants')
一些人对类属性的麻烦在于他们没有做出预期的事情 - 好像它是一个常数。它们不是常数。类属性更改了类的所有现有和未来实例的数据。有时有用,但可能令人困惑。
真的,如果class属性是可变的那么无关紧要。如您所见 - 您可以随时更改属性。