zoukankan      html  css  js  c++  java
  • python中dir,__dict__ , __setitem__(),__getitem__()

    class Testa:
        pass
    
    class Testb(object):
        pass
    
    if __name__ == '__main__':
        print 'testb = ',dir(Testb)
        print '
    '
        print 'testa = ',dir(Testa)

    运行结果:

    testb = ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']


    testa = ['__doc__', '__module__']

    class A(object):
        """
        Class A.
        """
        a = 0
        b = 1
    
        def __init__(self):
            self.a = 2
            self.b = 3
    
        def test(self):
            print 'a normal func.'
    
        @staticmethod
        def static_test(self):
            print 'a static func.'
    
        @classmethod
        def class_test(self):
            print 'a calss func.'
    
    obj = A()
    print A.__dict__
    print obj.__dict__

    运行结果:

    {'a': 0, '__module__': '__main__', 'b': 1, 'class_test': <classmethod object at 0x021FF510>, '__dict__': <attribute '__dict__' of 'A' objects>, '__init__': <function __init__ at 0x01E19DB0>, 'test': <function test at 0x022044B0>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': ' Class A. ', 'static_test': <staticmethod object at 0x021FF4D0>}


    {'a': 2, 'b': 3}

    由此可见,类和实例分别拥有自己的__dict__

    类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的

    对象的__dict__中存储了一些self.xxx的一些东西

    2、Python里什么没有__dict__属性

      虽然说一切皆对象,但对象也有不同,就好比不是每个人的女朋友都是一个人一样,一些内置的数据类型是没有__dict__属性的,如下:

    num = 3
    ll = []
    dd = {}
    print num.__dict__
    print ll.__dict__
    print dd.__dict__

    当我们运行这样的代码时,解释器就会告诉我们

    Traceback (most recent call last):
      File "f:python	est.py", line 54, in <module>
        print num.__dict__
    AttributeError: 'int' object has no attribute '__dict__'
    
    Traceback (most recent call last):
      File "f:python	est.py", line 55, in <module>
        print ll.__dict__
    AttributeError: 'list' object has no attribute '__dict__'
    
    Traceback (most recent call last):
      File "f:python	est.py", line 56, in <module>
        print dd.__dict__
    AttributeError: 'dict' object has no attribute '__dict__'

    int, list, dict等这些常用的数据类型是没有__dict__属性的,其实这是可预料的,就算给了它们dict属性也没啥用,毕竟它们只是用来做数据容器的。

    3、发生继承时候的__dict__属性

      子类有自己的__dict__, 父类也有自己的__dict__,子类的全局变量和函数放在子类的dict中,父类的放在父类dict中。

    class Parent(object):
        a = 0
        b = 1
    
        def __init__(self):
            self.a = 2
            self.b = 3
    
        def p_test(self):
            pass
    
    
    class Child(Parent):
        a = 4
        b = 5
    
        def __init__(self):
            super(Child, self).__init__()
            # self.a = 6 #重写父类的成员变量
            # self.b = 7
    
        def c_test(self):
            pass
    
        def p_test(self):
            pass
    
    
    p = Parent()
    c = Child()
    print Parent.__dict__
    print Child.__dict__
    print p.__dict__
    print c.__dict__

    运行结果:

    {'a': 0, '__module__': '__main__', 'b': 1, '__dict__': <attribute '__dict__' of 'Parent' objects>, 'p_test': <function p_test at 0x021B4530>, '__weakref__': <attribute '__weakref__' of 'Parent' objects>, '__doc__': None, '__init__': <function __init__ at 0x00879DB0>}
    {'a': 4, 'c_test': <function c_test at 0x021B44F0>, '__module__': '__main__', 'b': 5, 'p_test': <function p_test at 0x021B4570>, '__doc__': None, '__init__': <function __init__ at 0x021B4470>}
    {'a': 2, 'b': 3}
    {'a': 2, 'b': 3}

      1)上段输出结果中,用红色字体标出的是类变量和函数,由结果可知,每个类的类变量、函数名都放在自己的__dict__中

      2) 再来看一下实力变量的__dict__中,由蓝色字体标识,父类和子类对象的__dict__是公用的

    class Borg(object):
        __shared_state = {} #子类和父类共享这个变量,导致输出的结果是一样的,具有迷惑性
    
        def __init__(self):
            self.__dict__ = self.__shared_state
            self.state = 'Init'
    
        def __str__(self):
            return self.state
    
    class YourBorg(Borg):
        pass
    
    if __name__ == '__main__':
        rm1 = Borg()
        rm2 = Borg()
    
        rm1.state = 'Idle'
        rm2.state = 'Running'
    
        print('rm1: {0}'.format(rm1))
        print('rm2: {0}'.format(rm2))

    输出结果:

    rm1: Running
    rm2: Running

    上面这一段代码,乍看挺神奇的,Borg 的各个实例共享了state。实现起来也很巧妙,利用了__dict__。 我们知道,python中__dict__存储了该对象的一些属性。类和实例分别拥有自己的__dict__,且实例会共享类的__dict__。

    这里有一个我一直以来都搞混的知识点,在__init__ 中声明的变量 ,以及在方法体之外声明的变量分别是在哪里。很简单的测试就能得到,在__init__中,self.xxx = xxx会把变量存在实例的__dict__中,仅会在该实例中能获取到,

    而在方法体外声明的,会在class的__dict__中。

    总结:

      1) 内置的数据类型没有__dict__属性

      2) 每个类有自己的__dict__属性,就算存着继承关系,父类的__dict__ 并不会影响子类的__dict__

      3) 对象也有自己的__dict__属性, 存储self.xxx 信息,父子类对象公用__dict__

    __xxxitem__:使用 [''] 的方式操作属性时被调用

    __setitem__:每当属性被赋值的时候都会调用该方法,因此不能再该方法内赋值 self.name = value 会死循环

    __getitem__:当访问属性时会调用该方法

    __delitem__:当删除属性时调用该方法

    python 运算符重载__getitem__和__setitem__

    class Indexer:
        def __getitem__(self, index):    #重载索引,对于实例的索引运算,会自动调用__getitem__
            return index**2
    
    x = Indexer()
    print(x[3])
    
    class Indexer2:
        data=[1,2,3,4,5]
        def __getitem__(self, index): #重载索引,对于实例的索引运算,会自动调用__getitem__
            print '__getitem__'
            return self.data[index]
        def __setitem__(self, index, value): #重载索引赋值,对于实例的索引赋值,会自动调用__setitem__
            print 'setitem:'
            self.data[index]=value
        def __index__(self):
            print '__index__'
            return 255
    
    x=Indexer2()
    print(x[0])
    print x[::-1]
    print(x[1:])
    print(x[2:4])
    print(bin(x), bin(255))
    print(('a'*256)[x])
    x[0] = 102
    print x
    exit(0)

    运行结果:

    9
    __getitem__
    1
    __getitem__
    [5, 4, 3, 2, 1]
    __getitem__
    [2, 3, 4, 5]
    __getitem__
    [3, 4]
    __index__
    ('0b11111111', '0b11111111')
    __index__
    a
    setitem:
    <__main__.Indexer2 instance at 0x022C3D50>

    python中 __setitem__(),__getitem__()

    class Person:
    #将对象当作字典操作,设置键值对时会触发该方法
        def __setitem__(self, key, value):
            print(key,value)
            self.__dict__[key]=value
    
        #将对象当作字典操作,根据键获取值时会触发该方法
    
        def __getitem__(self, item):
            print(item)
            return self.__dict__.get(item)
    
        #将对象当作字典操作,删除指定的键值对时自动触发
    
        def __delitem__(self, key):
            del self.__dict__[key]
    
    xiaoming = Person()
    xiaoming['name'] = '小宏'
    print(xiaoming.__dict__)
    print(xiaoming['name'])
    del xiaoming['name']

    运行结果:

    ('name', 'xe5xb0x8fxe5xaex8f')
    {'name': 'xe5xb0x8fxe5xaex8f'}
    name
    小宏

    __setitem__(self,key,value):

    这个方法应该以与键相关联的方式存储值,以便之后能够使用__setitem__来获取。当然,这个对象可变时才需要实现这个方法。

    class Tag:
        def __init__(self):
            self.change = {'python': 'This is python',
                           'php': 'PHP is a good language'}
    
        def __getitem__(self, item):
            print('调用getitem')
            return self.change[item]
    
        def __setitem__(self, key, value):
            print('调用setitem')
            self.change[key] = value
    
    
    a = Tag()
    print(a['php'])
    a['php'] = 'PHP is not a good language'
    a['ruby'] = 'Ruby is not a good language'
    print(a['php'])
    print(a['ruby'])
    print a.__dict__
    exit(0)

    运行结果:

    调用getitem
    PHP is a good language
    调用setitem
    调用setitem
    调用getitem
    PHP is not a good language
    调用getitem
    Ruby is not a good language
    {'change': {'python': 'This is python', 'php': 'PHP is not a good language', 'ruby': 'Ruby is not a good language'}}

    __delitem__和__delattr__

    class Foo:
        def __init__(self,name):
            self.name=name
        def __getitem__(self, item):
            if item == 1:
                print('hahaha')
            # print(self.__dict__[item])
        def __setitem__(self, key, value):
            self.__dict__[key]=value
        def __delitem__(self, key):
            print('del obj[key]时,我执行')
            self.__dict__.pop(key)
        def __delattr__(self, item):
            print('del obj.key时,我执行')
            self.__dict__.pop(item)
    
    f1 = Foo('sb')
    #访问属性的方式变了
    #对象名.属性
    f1=Foo('sb')
    f1['age']=18  #给f1添加一个属性
    del f1['age']  #删除属性
    # f1.name
    print(f1['name'])
    
    f1.__dict__['age'] = 18
    f1['age1']=19
    del f1.age1   #删除属性
    
    f1['name']='alex'
    print(f1.__dict__)
    exit(0)

     输出结果:

    del obj[key]时,我执行
    None
    del obj.key时,我执行
    {'age': 18, 'name': 'alex'}

    使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。而如果要保持Key的顺序,可以用OrderedDict

    注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:

    OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key:

    from collections import OrderedDict
    
    class LastUpdatedOrderedDict(OrderedDict):
    
        def __init__(self, capacity):
            super(LastUpdatedOrderedDict, self).__init__()
            self._capacity = capacity
    
        def __setitem__(self, key, value):
            containsKey = 1 if key in self else 0
            if len(self) - containsKey >= self._capacity:
                last = self.popitem(last=False)
                print 'remove:', last
            if containsKey:
                del self[key]
                print 'set:', (key, value)
            else:
                print 'add:', (key, value)
            OrderedDict.__setitem__(self, key, value)
    
    # test
    d = LastUpdatedOrderedDict(capacity=3)
    
    d['A'] = 100
    d['B'] = 200
    d['C'] = 300
    d['D'] = 400
    d['E'] = 500
    print d

    运行结果:

    add: ('A', 100)
    add: ('B', 200)
    add: ('C', 300)
    remove: ('A', 100)
    add: ('D', 400)
    remove: ('B', 200)
    add: ('E', 500)
    LastUpdatedOrderedDict([('C', 300), ('D', 400), ('E', 500)])

    python中__len__

    #coding=utf-8
    class A:
    
        def __init__(self):
            self.a = 1
            self.b = 2
    
        def fun(self):
            pass
    
        def __len__(self):
            return len(self.__dict__)  #返回的是对象自身的长度
    
    a = A()
    print(len(a)) #2
    exit(0)

    _hash__

    class A:
        def __init__(self):
            self.a = 1
            self.b = 2
    
        def __hash__(self):
            return hash(str(self.a)+str(self.b))
    a = A()
    print(hash(a))  #当在外部使用hash()这种方法实际上就是在内部调用  __hash__这个方法
    exit(0)

    _EQ__

    #coding=utf-8
    class A:
        def __init__(self):
            self.a = 1
            self.b = 2
    
        def __eq__(self,obj):
            if  self.a == obj.a and self.b == obj.b:
                return True
    a=A()
    b=A()
    print(a==b)  #在外部执行==实际上就是在内部调用 __eq__方法
    exit(0)

    __call__

    对象后面加括号,触发执行。

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    #coding=utf-8
    class Foo:
    
        def __init__(self):
            pass
    
        def __call__(self, *args, **kwargs):
            print('__call__')
    
    obj = Foo()  # 执行 __init__
    obj()  # 执行 __call__
    exit(0)

    python __setitem__ 和 __setattr__关于无限递归

    对于__setattr__如果用如下的定义
    def __setattr__(self,name,value):
      self.name=value 
    它在出现类似self.a=b的赋值语句时会调用__setattr__并在函数中继续进行同样的赋值操作从而引起无限递归,也就是触发调用__setattr__的条件是遇到self.a=b这样的语句

    但对于__setitem__
    def __setitem__(self,key,value): 
      self.__dict__[key]=value
    如果定义了一个实例s,要使用序列的方法__setitem__,自然s会成一个字典,而如果我使用s[a]=b的语句修改字典的键的值时为何不会再在__setitem__中继续将a和b作为key和value进行修改赋值从而出现无限递归?
    触发调用__setitem__的条件应该是类似s[a]=b还是别的什么语句吗?
     
    def __setitem__(self,key,value): 
        self[key]=value  #以这种方式则会触发无限递归
         
    def __setitem__(self,key,value): 
        self.__dict__[key]=value  #这种则不会,它是以修改对象内置字典的键的值的方式来修改对象的属性值

    参考自:

    https://blog.csdn.net/qq_42467563/article/details/82974285

    https://www.jianshu.com/p/cf8450b4b537

    https://www.cnblogs.com/alvin2010/p/9102344.html

    https://blog.csdn.net/xhw88398569/article/details/48690163

  • 相关阅读:
    yii2 动态配置日志(log)
    Yii2 增删改查(CRUD)
    php curl数据传输神器
    Yii Cache 缓存的使用
    svn提交后 添加注释
    php file()函数
    maven + bat 实现快速编译打包模块代码
    获取SpringMVC所有的rest接口及其对应函数信息
    IntelliJ IDEA #region 代码折叠
    maven+Spring+SpringMVC+Hibernate快速搭建
  • 原文地址:https://www.cnblogs.com/leijiangtao/p/3771219.html
Copyright © 2011-2022 走看看