Python3动态错误报告

时间:2014-09-15 01:59:42

标签: python python-3.x dynamic

我们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'装饰器。这在某种程度上是荒谬的。

人们不应该同时测试我的测试用例

2 个答案:

答案 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)

@Dano的测试是合理的,但这是新的情况。那么我以这种方式测试的原因是,我希望在同一个类的整个对象之间共享可读数据,而不是在不同类(母类,子类等)之间共享的可写结果:

我用这样的方式写课:

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

这是我在pydev中获得的运行结果:

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中的继承覆盖问题!