我们ERIAN的团队正在使用python 3.4.1传输和开发代码。当我尝试在python3中测试动态属性时,我对以下动态错误感到震惊,其他程序员应该注意:
# -*- coding: utf-8 -*-
''' Created on 12 Sep, 2014
@author: wang yi/lei, Singapore '''
class foo(object):
dic = {}
def __init__(self):
print('self:', self)
self.dic[self.__str__()] = "foo"
def __go__(self, obj):
self.obj = obj()
def __str__(self):
return 'foo'
class bar(foo):
def __init__(self):
super(bar, self).__init__()
print('self', self)
print(self.dic)
def __str__(self):
return 'bar'
# Comment others, when you test one case
if __name__ == '__main__':
# test case 1: python will output parameters as expected
f = foo() ## self: foo
f.__go__(bar) ## self: bar \n self bar \n {'foo': 'foo', 'bar': 'foo'} #wrong !!!
# test case 2: python will output parameters as expected
b = bar() # self: bar \n self bar \n {'bar':'foo'}
# test case 3: python will go wrong! i.e. b will be associated an unexpected keyword pair 'foo':'foo'
f = foo() ## self: foo
b = bar() ## self: bar \n self: bar \n self bar \n {'foo': 'foo', 'bar': 'foo'} #wrong!!!
pass
我认为这个结果不是人们现实所需要的。你需要一个与超类对象(公共领域)无关的属性,对吗?有些人主张他或她的身份测试证明,这说明我实际上是在声明一个类属性,但是让事情变得棘手的是我没有使用'@static'装饰器。这在某种程度上是荒谬的。
人们不应该同时测试我的测试用例 !
答案 0 :(得分:2)
这种情况正在发生,因为您正在使dic
成为类变量而不是实例变量。这意味着dic
的所有实例以及foo
的子的所有实例之间共享完全相同的foo
对象:
>>> b = bar()
('self:', <f.bar object at 0x7f4c31af1950>)
('self', <f.bar object at 0x7f4c31af1950>)
{'bar': 'foo'}
>>> f = foo()
('self:', <f.foo object at 0x7f4c31af1b90>)
>>> f.dic
{'foo': 'foo', 'bar': 'foo'}
>>> b.dic
{'foo': 'foo', 'bar': 'foo'}
>>> f.dic is b.dic
True
>>> bar.dic
{'foo': 'foo', 'bar': 'foo'}
>>> foo.dic
{'foo': 'foo', 'bar': 'foo'}
如果您不想要此行为,则需要将dic
设为实例变量:
class foo(object):
def __init__(self):
self.dic = {} # Now every instance of `foo` and its children will have their own dic attribute
print('self:', self)
self.dic[self.__str__()] = "foo"
def __go__(self, obj):
self.obj = obj()
def __str__(self):
return 'foo'
现在你将得到你期望的行为:
>>> from f import foo, bar
>>> b = bar()
('self:', <f.bar object at 0x7f569172d8d0>)
('self', <f.bar object at 0x7f569172d8d0>)
{'bar': 'foo'}
>>> f = foo()
('self:', <f.foo object at 0x7f569172db50>)
>>> f.dic
{'foo': 'foo'}
>>> b.dic
{'bar': 'foo'}
>>> f.dic is b.dic
False
>>> foo.dic
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'foo' has no attribute 'dic'
>>> bar.dic
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'bar' has no attribute 'dic'
官方文档even specifically calls out this exact mistake:
正如关于名称和对象的单词中所讨论的,共享数据可以有 涉及可变对象的可能令人惊讶的效果,如 列表和词典。例如,技巧列表如下 代码不应该用作类变量,因为只有一个列表 将被所有Dog实例共享:
class Dog: tricks = [] # mistaken use of a class variable def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks # unexpectedly shared by all dogs ['roll over', 'play dead']
正确设计类应该使用实例变量:
class Dog: def __init__(self, name): self.name = name self.tricks = [] # creates a new empty list for each dog def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks ['roll over'] >>> e.tricks ['play dead'] ['roll over', 'play dead']
答案 1 :(得分:0)
我用这样的方式写课:
class mother(object):
param = {'mother':metadata} #mutable
def __init__():
bla bla bla
**********************
position = self.position[self.__name__()] #for dynamic purpose
clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass)
for k in self.hierarchicalkeys[position:]:
for m in clsmembers:
if k == m[0]:
self.hierarchicalchain.append(m[1])
break #jump out of the most inner loop
**********************
class monther'son(mother):
param = {"monther'son":metadata} #mutable
bla bla bla
class mother'son'son(mother'son):
param = {"mother'son'son":metadata} #mutable
bla bla bla
m: [<class '__main__.MonthData'>, <class '__main__.DayData'>, <class '__main__.HourData'>]
d: [<class '__main__.DayData'>, <class '__main__.HourData'>]
我在发布的问题中使用了相同的技巧。那么区别是什么呢?当您在子类中传递“self”时,它将引用子类对象本身!这意味着如果你覆盖子类中的param,你将得到不同的参数! @dano。
self.position is m.position
False
所以要覆盖子类中的param!这是共享可读数据的关键,而不考虑python中的继承覆盖问题!