Python - 特殊属性和方法浅析

__dict__

  • Python 中,一切皆对象
  • 不管是类还是实例的属性和方法,都符合 object.attribute 格式。并且属性类型
class A(object):
    pass
a = A()
dir(a)

输出:

    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__', '__module__','__ne__','__new__', '__reduce__', '__reduce_ex__','__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__','__weakref__']

class Spring(object):
    season = 'the spring of class'
Spring.__dict__

输出:

    mappingproxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
                  '__doc__': None,
                  '__module__': '__main__',
                  '__weakref__': <attribute '__weakref__' of 'Spring' objects>,
                  'season': 'the spring of class'})


#访问的是类属性
print(Spring.__dict__['season'])
print(Spring.season)

输出:

 'the spring of class'

#访问的是实例属性,因为class中没有定义实例属性所以打印为空
s = Spring()
s.__dict__

输出:

 {}

s.season    #访问的类属性

输出:

 'the spring of class'

#为class 的实例 中添加一个 season 属性
s.season = "instance";
s.__dict__

输出:

{'season': 'instance'}

#当类属性和实例属性重名时,实例属性会覆盖掉类属性
s.__dict__['season']

输出:

'instance'

#但是类属性不会变
Spring.__dict__['season']

输出:

'the spring of class'

Spring.season

输出:

 'the spring of class'

#删除实例属性后,就回到了实例化类的时候的状态,没有实例属性,只有类属性
del s.season
s.__dict__

输出:

    {}

s.season  #类属性

输出:

    'the spring of class'

#属性和方法是同样的
class Spring_def(object):
    def tree(self,x):
        self.x= x
        return self.x

Spring_def.__dict__

输出:

 mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_def' objects>,
                  '__doc__': None,
                  '__module__': '__main__',
                  '__weakref__': <attribute '__weakref__' of 'Spring_def' objects>,
                  'tree': <function __main__.Spring_def.tree>})

Spring_def.__dict__['tree']

输出:

    <function __main__.Spring_def.tree>


t = Spring_def()
t.__dict__

输出:

    {}

t.tree('value')
t.__dict__

输出:

    {}

class Spring_no(object):
    def tree(self,x):
        return x

ts = Spring_no()
ts.tree('No arg')
ts.__dict__

输出:

    {}

Spring_no.__dict__

输出:

    mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_no' objects>,
                  '__doc__': None,
                  '__module__': '__main__',
                  '__weakref__': <attribute '__weakref__' of 'Spring_no' objects>,
                  'tree': <function __main__.Spring_no.tree>})

__slots__

  • 能够限制属性的定义
  • 优化内存使用
  • __dict__替换成__slots__
  • 用类给一个属性赋值后,实例就不能给这个属性赋值了。
  • 也不能新增实例属性,这样就把属性管控了起来。内存得到优化
  • 大量实例的话会显现出效果
class Spring_s(object):
    __slots__ = ("tree","flows")
dir(Spring_s)

输出:

    ['__class__','__delattr__','__dir__','__doc__','__eq__', '__format__','__ge__','__getattribute__', '__gt__','__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',  '__slots__', '__str__',   '__subclasshook__', 'flows',  'tree']

Spring_s.__dict__
输出:
mappingproxy({'__doc__': None,
                  '__module__': '__main__',
                  '__slots__': ('tree', 'flows'),
                  'flows': <member 'flows' of 'Spring_s' objects>,
                  'tree': <member 'tree' of 'Spring_s' objects>})

Spring_s.__slots__

输出:

    ('tree', 'flows')

ts = Spring_s()
ts.__slots__

输出:

    ('tree', 'flows')

#使用__slots__后就没有__dict__ 属性了
ts.__dict__

输出:

#错误
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-40-f3db88226dd5> in <module>()
          1 #使用__slots__后就没有__dict__ 属性了
    ----> 2 ts.__dict__
    

    AttributeError: 'Spring_s' object has no attribute '__dict__'

Spring_s.tree = "liushu"
Spring_s.tree

输出:

    'liushu'

# 报错中显示 实例属性 tree 只是制度的,不能修改
ts.tree = "yangshu"

输出:

#错误
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-42-d6b9cb191c20> in <module>()
          1 # 报错中显示 实例属性 tree 只是制度的,不能修改
    ----> 2 ts.tree = "yangshu"
    

    AttributeError: 'Spring_s' object attribute 'tree' is read-only


ts.tree

输出:

    'liushu'

  • 前面已经通过类给属性赋值了,就不能用实例属性来修改。
  • 当使用实例给属性赋值后,还是可以再用实例来给属性赋值,但是当这个属性被类赋值后,就不能再用实例给属性赋值了,只能用类来给属性赋值.
#第一次实例赋值
ts.flows = "meigui"
ts.flows

输出:

    'meigui'

#第二次实例赋值
ts.flows = "yueji"
ts.flows

输出:

    'yueji'

#使用类赋值
Spring_s.flows = "shuixianhua"
Spring_s.flows

输出:

 'shuixianhua'

#实例的属性的值会变成类属性的值
ts.flows

输出:

    'shuixianhua'

#再用实例给属性赋值就会报只读错误
ts.flows = "qianniuhua"

输出:

#错误
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-48-726c06affe50> in <module>()
          1 #再用实例给属性赋值就会报只读错误
    ----> 2 ts.flows = "qianniuhua"
    

    AttributeError: 'Spring_s' object attribute 'flows' is read-only


Spring_s.flows = "baihe"
Spring_s.flows

输出:

    'baihe'

ts.flows

输出:

    'baihe'

  • 实例中添加属性
# 新增实例失败 
ts.water = "green"

输出:

#错误
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-52-cde7efd2f5b8> in <module>()
    ----> 1 ts.water = "green"
    

    AttributeError: 'Spring_s' object has no attribute 'water'

__getattr____setattr__ 和其他类似方法

  • __setattr__(self,name,value): 如果要给name赋值,就调用这个方法
  • __getattr__(self,name): 如果name被访问,同事它不存在,此方法被调用
  • __getattribute__(self,name): 当name被访问时自动被调用(注意:这个仅能用于新式类),无论name是否存在,都要被调用
  • __delattr__(self,name): 如果要删除name,这个方法就被调用。
class B(object):
    pass
b = B()
b.x 

输出:

    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-42-f8f5c58b62c7> in <module>()
          2     pass
          3 a = A()
    ----> 4 a.x
    

    AttributeError: 'A' object has no attribute 'x'

x 不是势力的成员(属性和方法)所以报错。也就是说,如果访问 b.x ,它不存在,那么就要转向到某个操作 ,我们把这种情况叫做“拦截”。

class C(object):
    def __getattr__(self,name):
        print('you use getattr')
    def __setattr__(self,name,value):
        print('you use setattr')
        self.__dict__[name] = value

类C是新式类,出来2个方法,没有别的属性

c = C()
c.x

输出:

    you use getattr

本来按照开头class B 是要报错的。但是由于这里使用了 __getattr__(self,name) 当发现x不存在与对象__dict__ 时,就会调用__getattr“拦截成员” 。

c.x = 7

输出:

    you use setattr

给对象的属性赋值是,调用了__setattr__(self,name,value)方法,这个方法有一句 self.__dict__[name] = value通过这个语句,就将属性和数据保存到了对象的 __dict__中。如果再调用这个属性:

c.x  # x 已经存在于对象的 __dict__中

输出:

    7

使用__getattribute__(self,name):,只要访问属性就会调用它

class getAt(object):
    def __getattribute__(self,name):
        print('使用了getattribute')
        return object.__getattribute__(self,name)
at = getAt()
at.y

输出:

    使用了getattribute



    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-54-6e81b53b32de> in <module>()
          1 at = getAt()
    ----> 2 at.y
    

    <ipython-input-53-a6779a4b213a> in __getattribute__(self, name)
          2     def __getattribute__(self,name):
          3         print('使用了getattribute')
    ----> 4         return object.__getattribute__(self,name)
    

    AttributeError: 'getAt' object has no attribute 'y'

  • 访问一个不存在的属性,虽然拦截了但还是报错了。
  • 但是只要赋值后就可以访问了,因为这样就意味着做个属性已经存在于__dict__中了。虽然依然被拦截,但是会把结果返回。
at.y = 9
at.y

输出:

    使用了getattribute
    9

注意:这里没有 return self.__dict__[name],因为如果用这样的方法访问self.__dict__只要访问这个方法,就会去调用__getattribute__,这样就会无限递归下去,造成死循环。

示例:
''' study __getattr__  and  __setattr__'''
class Rectangle(object):
    ''' the width  and length of Rectangle'''
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self,size):
        self.width,self.height = size
    def getSize(self):
        return self.width,self.height
if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.height = 4
    print (r.getSize())
    r.setSize((30,40))
    print(r.width)
    print(r.height)

输出:

    (3, 4)
    30
    40

长宽赋值的时候必须是一个元组,里面包含长宽。改进。。。

class Rectangle_2(object):
    ''' 使用property改进调用方法传参'''
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self,size):
        self.width,self.height = size
    def getSize(self):
        return self.width,self.height
    #---------新加---------#
    size = property(getSize,setSize)
    #---------结束---------#
if __name__ == "__main__":
    r = Rectangle_2()
    r.width = 3
    r.length = 4
    print(r.size)
    r.size = 30, 40
    print (r.width)
    print (r.length)

输出:

    (3, 0)
    30
    4

虽然方法调用就像属性一样,但是没有用上特殊方法。继续改进。。。

class Rectangle_3(object):
    '''使用特殊方法'''
    def __init__(self):
        self.width = 0
        self.height = 0
    def __setattr__(self, name, value):
        if name == "size":
            self.width, self.height = value
        else:
            self.__dict__[name] = value
    def __getattr__(self, name):
        if name == "size":
            return self.width,self.height
        else:
            return AttributeError
    
if __name__ == "__main__":
    nr = Rectangle_3()
    nr.width = 3
    nr.height = 4
    print(nr.size)
    nr.size = 30,40
    print(nr.width)
    print(nr.height)

输出:

    (3, 4)
    30
    40

获得属性顺序

  • 通过实例获取其属性,如果在__dict__中有,就直接返回其结果,如果没有,就会到类属性中找。
class search(object):
    author = 'Python'
    def __getattr__(self , name):
        if name != 'author':
            return '查询的不是author属性'
if __name__ == '__main__':
    a = search()
    print(a.author)
    print(a.none)
    print(a.__dict__)

输出:

    Python
    查询的不是author属性
    {}

  • 初始化后 a 没有建立任何实例属性。实例属性__dict__是空,没有属性值,a.author 则有值,因为是类属性,而实例中又没有,所以就去类属性中去找,所以就返回了'Python'
  • 当a.none 的时候不仅实例中没有,类中也没有,所以就调用了__getattr__方法。如果不写__getattr__方法就会报错了。
  • 这就是 通过实例查找特性的顺序

参考:《跟着老齐学Python:从入门到精通》 作者:齐伟 电子工业出版社

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,771评论 1 10
  • 20- 枚举,枚举原始值,枚举相关值,switch提取枚举关联值 Swift枚举: Swift中的枚举比OC中的枚...
    iOS_恒仔阅读 2,235评论 1 6
  • 1、什么叫魔法方法? 魔法方法:Python解释器自动给出默认的,是可以给你的类增加魔力的特殊方法。如果你的对象实...
    Bling_ll阅读 1,034评论 0 2
  • 要点: 函数式编程:注意不是“函数编程”,多了一个“式” 模块:如何使用模块 面向对象编程:面向对象的概念、属性、...
    victorsungo阅读 1,459评论 0 6
  • 曾经听到这样一个故事。一个渔夫在沙滩上晒太阳。 一个富人走过,问他:“你为什么躺在这里晒太阳,不去补网...
    磊磊popo阅读 684评论 13 3