1

有人告诉我,在类范围内声明动态属性不是“Python 方式”,但我不明白为什么。有人可以向我解释一下,或者向我指出一些文档,说明为什么这是一件坏事吗?老实说,我认为这是一个很好的做法,如果对自我记录代码有任何帮助的话。

例子:

class ClassA(object):
    user_data = {}

    def set_user(self):
        self.user_data['username'] = 'fred'

我可以看到不使用它的唯一原因是属性是静态的(因此可能会产生误导)..

4

4 回答 4

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 无关。这是编写按照您的意愿运行的代码的问题。

于 2013-02-11T16:10:30.773 回答
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,如果我添加 items: foo_instance.whatever['foo'] = 'bar',那么foo_instance2也会看到这种变化,而使用Bar.

于 2013-02-11T15:51:52.250 回答
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方式”的问题。仅此而已,意见。有些事情会被一些人讨厌,而不是其他人。你应该以一种让你感到舒适、适合你的方式写作,只要你符合你所在的任何劳动力所规定的法规和标准。

于 2013-02-11T15:53:20.260 回答
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

请注意, Example 的每个实例都具有相同的 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')

有些人对类属性的问题是他们没有做预期的事情——就好像它是一个常数一样。它们不是常数。类属性更改类的所有现有和未来实例的数据。有时有用,但可能令人困惑。

实际上,类属性是否可变并不重要。如您所见——您可以随时更改属性。

于 2013-02-11T16:47:43.023 回答