zoukankan      html  css  js  c++  java
  • 深入理解python对象及属性

    类属性和实例属性
    首先来看看类属性和类实例的属性在python中如何存储,通过__dir__方法来查看对象的属性

    >>> class Test(object):
    pass
    >>> test = Test(http://www.my516.com)
    # 查看类属性
    >>> dir(Test)
    ['__class__','__delattr__','__dict__','__doc__','__format__',
    '__getattribute__', '__hash__', '__init__', '__module__',
    '__new__', '__reduce__', '__reduce_ex__', '__repr__',
    '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
    '__weakref__']
    # 查看实例属性
    >>> dir(test)
    ['__class__', '__delattr__', '__dict__', '__doc__', '__format__',
    '__getattribute__', '__hash__', '__init__', '__module__',
    '__new__', '__reduce__', '__reduce_ex__', '__repr__',
    '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
    '__weakref__']
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    我们主要看一个属性__dict__,因为 __dict__保存的对象的属性,看下面一个例子

    >>> class Spring(object):
    ... season = "the spring of class"
    ...

    # 查看Spring类保存的属性
    >>> Spring.__dict__
    dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
    'season': 'the spring of class',
    '__module__': '__main__',
    '__weakref__': <attribute '__weakref__' of 'Spring' objects>,
    '__doc__': None})

    # 通过两种方法访问类属性
    >>> Spring.__dict__['season']
    'the spring of class'
    >>> Spring.season
    'the spring of class'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    发现__dict__有个’season’键,这就是这个类的属性,其值就是类属性的数据.
    接来看,看看它的实例属性

    >>> s = Spring()
    # 实例属性的__dict__是空的
    >>> s.__dict__
    {}
    # 其实是指向的类属性
    >>> s.season
    'the spring of class'

    # 建立实例属性
    >>> s.season = "the spring of instance"
    # 这样,实例属性里面就不空了。这时候建立的实例属性和类属性重名,并且把它覆盖了
    >>> s.__dict__
    {'season': 'the spring of instance'}
    >>> s.__dict__['season']
    'the spring of instance'
    >>> s.season
    'the spring of instance'

    # 类属性没有受到实例属性的影响
    >>> Spring.__dict__['season']
    'the spring of class'
    >>> Spring.__dict__
    dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>, 'season': 'the spring of class', '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__doc__': None})

    # 如果将实例属性删除,又会调用类属性
    >>> del s.season
    >>> s.__dict__
    {}
    >>> s.season
    'the spring of class'

    # 自定义实例属性,对类属性没有影响
    >>> s.lang = "python"
    >>> s.__dict__
    {'lang': 'python'}
    >>> s.__dict__['lang']
    'python'

    # 修改类属性
    >>> Spring.flower = "peach"
    >>> Spring.__dict__
    dict_proxy({'__module__': '__main__',
    'flower': 'peach',
    'season': 'the spring of class',
    '__dict__': <attribute '__dict__' of 'Spring' objects>, '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__doc__': None})
    >>> Spring.__dict__['flower']
    'peach'
    # 实例中的__dict__并没有变化
    >>> s.__dict__
    {'lang': 'python'}
    # 实例中找不到flower属性,调用类属性
    >>> s.flower
    'peach'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    下面看看类中包含方法,__dict__如何发生变化

    # 定义类
    >>> class Spring(object):
    ... def tree(self, x):
    ... self.x = x
    ... return self.x
    ...
    # 方法tree在__dict__里面
    >>> Spring.__dict__
    dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
    '__weakref__': <attribute '__weakref__' of 'Spring' objects>,
    '__module__': '__main__',
    'tree': <function tree at 0xb748fdf4>,
    '__doc__': None})
    >>> Spring.__dict__['tree']
    <function tree at 0xb748fdf4>

    # 建立实例,但是__dict__中没有方法
    >>> t = Spring()
    >>> t.__dict__
    {}

    # 执行方法
    >>> t.tree("xiangzhangshu")
    'xiangzhangshu'
    # 实例方法(t.tree('xiangzhangshu'))的第一个参数(self,但没有写出来)绑定实例 t,透过 self.x 来设定值,即给 t.__dict__添加属性值。
    >>> t.__dict__
    {'x': 'xiangzhangshu'}
    # 如果没有将x 赋值给 self 的属性,而是直接 return,结果发生了变化
    >>> class Spring(object):
    ... def tree(self, x):
    ... return x
    >>> s = Spring()
    >>> s.tree("liushu")
    'liushu'
    >>> s.__dict__
    {}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    需要理解python中的一个观点,一切都是对象,不管是类还是实例,都可以看成是对象,符合object.attribute ,都会有自己的属性

    使用__slots__优化内存使用
    默认情况下,python在各个实例中为名为__dict__的字典里存储实例属性,而字典会消耗大量内存(字典要使用底层散列表提升访问速度), 通过__slots__类属性,在元组中存储实例属性,不用字典,从而节省大量内存

    # 在类中定义__slots__属性就是说这个类中所有实例的属性都在这儿了,如果几百万个实例同时活动,能节省大量内存
    >>> class Spring(object):
    ... __slots__ = ("tree", "flower")
    ...
    # 仔细看看 dir() 的结果,还有__dict__属性吗?没有了,的确没有了。也就是说__slots__把__dict__挤出去了,它进入了类的属性。
    >>> dir(Spring)
    ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'flower', 'tree']
    >>> Spring.__slots__
    ('tree', 'flower')
    # 实例化
    >>> t = Spring()
    >>> t.__slots__
    ('tree', 'flower')

    # 通过类赋予属性值
    >>> Spring.tree = "liushu"
    # tree这个属性是只读的, 实例不能修改
    >>> t.tree = "guangyulan"
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'Spring' object attribute 'tree' is read-only
    >>> t.tree
    'liushu'

    # 对于用类属性赋值的属性,只能用来修改
    >>> Spring.tree = "guangyulan"
    >>> t.tree
    'guangyulan'

    # 对于没有用类属性赋值的属性,可以通过实例来修改
    >>> t.flower = "haitanghua"
    >>> t.flower
    'haitanghua'
    # 实例属性的值并没有传回到类属性,你也可以理解为新建立了一个同名的实例属性
    >>> Spring.flower
    <member 'flower' of 'Spring' objects>
    # 如果再给类属性赋值
    >>> Spring.flower = "ziteng"
    >>> t.flower
    'ziteng'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    如果使用的当,__slots__可以显著节省内存,按需要注意一下问题

    在类中定义__slots__之后,实例不能再有__slots__所列名称之外的其他属性
    每个子类都要定义__slots__熟悉,因为解释器会忽略继承__slots__属性
    如果不把__werkref__加入__slots__,实例不能作为弱引用的目标
    属性的魔术方法
    来看几个魔术方法

    __setattr__(self,name,value):如果要给 name 赋值,就调用这个方法。
    __getattr__(self,name):如果 name 被访问,同时它不存在的时候,此方法被调用。
    __getattribute__(self,name):当 name被访问时自动被调用(注意:这个仅能用于新式类),无论 name 是否存在,都要被调用。
    __delattr__(self,name):如果要删除 name,这个方法就被调用。
    >>> class A(object):
    ... def __getattr__(self, name):
    ... print "You use getattr"
    ... def __setattr__(self, name, value):
    ... print "You use setattr"
    ... self.__dict__[name] = value
    # a.x,按照本节开头的例子,是要报错的。但是,由于在这里使用了__getattr__(self, name) 方法,当发现 x 不存在于对象的__dict__中的时候,就调用了__getattr__,即所谓“拦截成员”。
    >>> a = A()
    >>> a.x
    You use getattr

    # 给对象的属性赋值时候,调用了__setattr__(self, name, value)方法,这个方法中有一句 self.__dict__[name] = value,通过这个语句,就将属性和数据保存到了对象的__dict__中
    >>> a.x = 7
    You use setattr

    # 测试__getattribute__(self,name)
    >>> class B(object):
    ... def __getattribute__(self, name):
    ... print "you are useing getattribute"
    ... return object.__getattribute__(self, name)
    # 返回的内容用的是 return object.__getattribute__(self, name),而没有使用 return self.__dict__[name]。因为如果用这样的方式,就是访问 self.__dict__,只要访问这个属性,就要调用`getattribute``,这样就导致了无限递归

    # 访问不存在的成员,可以看到,已经被__getattribute__拦截了,虽然最后还是要报错的。
    >>> b = B()
    >>> b.y
    you are useing getattribute
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 4, in __getattribute__
    AttributeError: 'B' object has no attribute 'y'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    Property函数
    porperty可以作为装饰器使用把方法标记为特性

    class Vector(object):
    def __init__(self, x, y):
    # 使用两个前导下划线,把属性标记为私有
    self.__x = float(x)
    self.__y = float(y)

    # porperty装饰器把读值方法标记为特性
    @property
    def x(self):
    return self.__x

    @property
    def y(self):
    return self.__y

    vector = Vector(3,4)
    print(vector.x, vector.y)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    使用property可以将函数封装为属性

    class Rectangle(object):
    """
    the width and length of Rectangle
    """
    def __init__(self):
    self.width = 0
    self.length = 0

    def setSize(self, size):
    self.width, self.length = size
    def getSize(self):
    return self.width, self.length

    if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.length = 4
    print r.getSize() # (3,4)
    r.setSize( (30, 40) )
    print r.width # 30
    print r.length # 40
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    这段代码可以正常运行,但是属性的调用方式可以改进,如下:

    class Rectangle(object):
    """
    the width and length of Rectangle
    """
    def __init__(self):
    self.width = 0
    self.length = 0

    def setSize(self, size):
    self.width, self.length = size
    def getSize(self):
    return self.width, self.length
    # 使用property方法将函数封装为属性,更优雅
    size = property(getSize, setSize)

    if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.length = 4
    print r.size # (30, 40)
    r.size = 30, 40
    print r.width # 30
    print r.length # 40
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    使用魔术方法实现:

    class NewRectangle(object):
    def __init__(self):
    self.width = 0
    self.length = 0

    def __setattr__(self, name, value):
    if name == 'size':
    self.width, self, length = value
    else:
    self.__dict__[name] = value

    def __getattr__(self, name):
    if name == 'size':
    return self.width, self.length
    else:
    raise AttrubuteErrir

    if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.length = 4
    print r.size # (30, 40)
    r.size = 30, 40
    print r.width # 30
    print r.length # 40
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    属性的获取顺序
    最后我们来看看熟悉的获得顺序:通过实例获取其属性,如果在__dict__中有相应的属性,就直接返回其结果;如果没有,会到类属性中找。
    看下面一个例子:

    class A(object):
    author = "qiwsir"
    def __getattr__(self, name):
    if name != "author":
    return "from starter to master."

    if __name__ == "__main__":
    a = A()
    print a.author # qiwsir
    print a.lang # from starter to master.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    当 a = A() 后,并没有为实例建立任何属性,或者说实例的__dict__是空的。但是如果要查看 a.author,因为实例的属性中没有,所以就去类属性中找,发现果然有,于是返回其值 “qiwsir”。但是,在找 a.lang的时候,不仅实例属性中没有,类属性中也没有,于是就调用了__getattr__()方法。在上面的类中,有这个方法,如果没有__getattr__()方法呢?如果没有定义这个方法,就会引发 AttributeError,这在前面已经看到了。
    --------------------- 

  • 相关阅读:
    开发用于异构环境的可生存云多机器人框架
    RVIZ实现模拟控制小车
    airsim 无法打开包括文件corecrt.h
    RoboWare Studio 安装
    Rosserial实现Windows-ROS交互操作
    nuix .pl文件运行
    OSError:[Errno 13] Permission denied:'my_library' 问题解决方法
    ros 运行rviz时出现 QXcbConnection: XCB error: 148 错误 解决方法
    在linux下一般用scp这个命令来通过ssh传输文件
    unix下命令窗分屏工具
  • 原文地址:https://www.cnblogs.com/hyhy904/p/11007509.html
Copyright © 2011-2022 走看看