python笔记整理-12. 再谈属性

python里的对象是基于字典的,属性的应用很灵活,不像java,类可以调用属性A.attra,对象也可以调用属性a.attra,在类里有self.attra也有直接定义在类里的attra,那么这些到底是什么区别,怎么使用呢,下面来分别举例说明.

例子1

class MyClass(object):
    attr_a = 1
    def __init__(self):
        pass

if __name__ == '__main__':
    mca = MyClass()
    mcb = MyClass()

    print(mca.attr_a)
    print(mcb.attr_a)
    print(MyClass.attr_a)

结果为

1
1
1

例子2

做一下修改

class MyClass(object):
    attr_a = 1
    def __init__(self):
        pass

if __name__ == '__main__':
    mca = MyClass()
    mcb = MyClass()

    mca.attr_a = 3

    print(mca.attr_a)
    print(mcb.attr_a)
    print(MyClass.attr_a)

结果

3
1
1

例子3

class MyClass(object):
    attr_a = 1
    def __init__(self):
        pass

if __name__ == '__main__':
    mca = MyClass()
    mcb = MyClass()

    mca.attr_a = 3

    MyClass.attr_a = 4

    print(mca.attr_a)
    print(mcb.attr_a)
    print(MyClass.attr_a)

结果是

3
4
4

在使用对象属性的时候,先去查找类里的属性,如果有就引用这个值.

例子4

class MyClass(object):
    attr_a = 1
    def __init__(self):
        pass

if __name__ == '__main__':
    mca = MyClass()
    mcb = MyClass()

    MyClass.attr_b = 10

    print(mca.attr_b)
    print(mcb.attr_b)
    print(MyClass.attr_b)

结果

10
10
10

如果类增加属性,对象同样可以获取的到

例子5

class MyClass(object):
    attr_a = 1
    def __init__(self):
        self.attr_c = 20

if __name__ == '__main__':
    mca = MyClass()
    mcb = MyClass()

    print(mca.attr_c)
    print(mcb.attr_c)
    print(MyClass.attr_c)

结果

20
20
Traceback (most recent call last):
  File "C:\Users\BBS-AAA\Desktop\tmp\2015-08\test\testpartial.py", line 12, in <module>
    print(MyClass.attr_c)
AttributeError: type object 'MyClass' has no attribute 'attr_c'

前两个值是可以获取的到的,但是最后一个报错了

那么做个试验

class MyClassA(object):
    attr_a = 1

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        self.attr_c = 20

if __name__ == '__main__':

    print(MyClassA.__dict__)
    print(MyClassB.__dict__)

结果如下

{'__doc__': None, '__weakref__': <attribute '__weakref__' of 'MyClassA' objects>, '__module__': '__main__', 'attr_a': 1, '__dict__': <attribute '__dict__' of 'MyClassA' objects>}
{'__doc__': None, '__init__': <function MyClassB.__init__ at 0x0000000001F5E9D8>, '__weakref__': <attribute '__weakref__' of 'MyClassB' objects>, '__module__': '__main__', 'attr_a': 1, '__dict__': <attribute '__dict__' of 'MyClassB' objects>}

可以看到MyClassB比A多了一个init的方法,而这个方法在没有调用的时候,是不会有attr_c的值的,所以上面的例子会报错.

例子6

class MyClassA(object):
    attr_a = 1

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        self.attr_a = 20

if __name__ == '__main__':

    mcb1 = MyClassB()

    print(mcb1.attr_a)

    print(mcb1.__dict__)

结果为

20
{'attr_a': 20}

例子7

class MyClassA(object):
    attr_a = 1

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        MyClassB.attr_a = 20

if __name__ == '__main__':

    mcb1 = MyClassB()

    print(mcb1.attr_a)

    print(mcb1.__dict__)

结果

20
{}

再生动点

class MyClassA(object):
    attr_a = 1

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        MyClassB.attr_a = 20

if __name__ == '__main__':

    mcb1 = MyClassB()

    mca1 = MyClassA()

    print(mca1.attr_a)
    print(mca1.__dict__)

    print(mcb1.attr_a)

    print(mcb1.__dict__)

结果

1
{}
20
{}

如果我们给实例设置属性呢

class MyClassA(object):
    attr_a = 1

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        MyClassB.attr_a = 20

if __name__ == '__main__':

    mcb1 = MyClassB()

    mca1 = MyClassA()

    print(mca1.attr_a)
    print(mca1.__dict__)

    print(mcb1.attr_a)
    print(mcb1.__dict__)

    mca1.attr_b = 30
    print(mca1.attr_b)
    print(mca1.__dict__)    

结果

1
{}
20
{}
30
{'attr_b': 30}

所以在此review,当我们改变一个实例的属性

class MyClassA(object):
    attr_a = 1

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        MyClassB.attr_a = 20

if __name__ == '__main__':


    mca1 = MyClassA()

    print(mca1.attr_a)
    print(mca1.__dict__)

    mca1.attr_a = 40
    print(mca1.attr_a)
    print(MyClassA.attr_a)
    print(mca1.__dict__)    
    print(MyClassA.__dict__)


结果

1
{}
40
1
{'attr_a': 40}
{'attr_a': 1, '__dict__': <attribute '__dict__' of 'MyClassA' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'MyClassA' objects>, '__module__': '__main__'}

这个时候实例里的这个属性已经不再引用类属性了.

但是要注意以下情况

class MyClassA(object):
    attr_a = {'a': 'a1'}

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        MyClassB.attr_a = 20

if __name__ == '__main__':


    mca1 = MyClassA()

    print(mca1.attr_a)
    print(mca1.__dict__)

    mca1.attr_a.update({'b': 'b1'})
    print(mca1.attr_a)
    print(MyClassA.attr_a)
    print(mca1.__dict__)    
    print(MyClassA.__dict__)

结果

{'a': 'a1'}
{}
{'a': 'a1', 'b': 'b1'}
{'a': 'a1', 'b': 'b1'}
{}
{'__doc__': None, 'attr_a': {'a': 'a1', 'b': 'b1'}, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'MyClassA' objects>, '__dict__': <attribute '__dict__' of 'MyClassA' objects>}

如果是修改为不可变值

class MyClassA(object):
    attr_a = {'a': 'a1'}

class MyClassB(object):
    attr_a = 1
    def __init__(self):
        MyClassB.attr_a = 20

if __name__ == '__main__':


    mca1 = MyClassA()

    print(mca1.attr_a)
    print(mca1.__dict__)

    mca1.attr_a = 'aabb'
    print(mca1.attr_a)
    print(MyClassA.attr_a)
    print(mca1.__dict__)    
    print(MyClassA.__dict__)

{'a': 'a1'}
{}
aabb
{'a': 'a1'}
{'attr_a': 'aabb'}
{'__dict__': <attribute '__dict__' of 'MyClassA' objects>, '__weakref__': <attribute '__weakref__' of 'MyClassA' objects>, 'attr_a': {'a': 'a1'}, '__module__': '__main__', '__doc__': None}

如果引用的是一个可变对象,类的属性也会跟着变,如果不是,那么实例会添加一个名称相同的值不同的属性
由于从对象的角度,类对象和实例对象属于两个独立的对象

总结

  1. Python中属性的获取是按照从下到上的顺序来查找属性;
  2. Python中的类和实例是两个完全独立的对象;
  3. Python中的属性设置是针对对象本身进行的;

参考

  • http://bluecrystal.iteye.com/blog/234923
  • http://segmentfault.com/a/1190000002671941
  • http://python.jobbole.com/81108/
  • http://my.oschina.net/taisha/blog/64084