zoukankan      html  css  js  c++  java
  • 魔法类

    python在实现类时有很多魔法,这里做个汇总。

    __call__

    在调用该方法时,无需显示地写出方法名

    class Student(object):
        def __init__(self, name):
            self.name = name
    
        def m(self):
            print(3)
    
        def __call__(self):
            self.data=3
            print('My name is %s.' % self.name)
    
    
    s = Student('Michael')
    s.m()   # 3                     显示地写出方法名
    s()     # My name is Michael.   无需显示地写出方法名

    __dict__

    含义见代码

    class B(object):
        foo=1.3
    print(B.__dict__)       # 类属性
    # {'__dict__': <attribute '__dict__' of 'B' objects>,
    # '__module__': '__main__',
    # 'foo': 1.3, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}
    
    b=B()
    print b.__dict__  # {}  # 实例属性
    
    b.bar=13
    print b.__dict__  # {'bar': 13}
    print b.bar  # 13
    ###
    # __dict__返回的是 dict类型,包括属性名和属性值
    # 类的__dict__存储类的属性方法,不包括实例的
    # 实例的__dict__只存储实例的属性方法,不包括类的,这点要注意。
    
    print dir(B)
    print dir(b)
    ###
    # dir 返回的是一个list,只包括属性值
    # dir 返回的是一个对象的所有属性

    __getattr__

    在访问不存在的属性和方法时,调用__getattr__方法

    # 访问不存在的属性
    class Student(object):
    
        def __init__(self):
            self.name = 'Michael'
    
        def __getattr__(self, attr):
            if attr=='score':
                return 99
    
    s = Student()
    print s.name        # 'Michael'
    print s.score       # 99            # 不存在的属性去getattr里找
    print s.t           # None          # getattr里找不到返回None,不报错
    
    
    # 访问不存在的方法
    class Student(object):
    
        def __getattr__(self, attr):
            if attr=='age':
                return lambda: 25       # 返回了函数的引用
    
    s = Student()
    print s.age()   # 25        # 方法也在getattr中找,但是找到的那个方法必须返回 函数 的引用
    print s.age     # <function <lambda> at 0x026672B0>
    # print s.ss()  # TypeError: 'NoneType' object is not callable

    __getattribute__

    属性拦截器

    在python中,所有类都要继承于object,object有很多内建的属性和方法,我们自定义的类自然也继承了这些属性和方法,但是这些属性方法很少被用到,而且很多属性方法需要被用户重写才能使用,__getattribute__就是其中一个。

    先上代码

    class Student(object):
        country = "china"
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
            print("开始属性校验拦截功能")
            print(attr)
            return object.__getattribute__(self, attr) #返回属性值
    
    s1 = Student("tom",19)
    print(Student.country,s1.country,s1.name,s1.age) #调用属性,会调用__getattribute__方法
    
    # 开始属性校验拦截功能
    # country
    # 开始属性校验拦截功能
    # name
    # 开始属性校验拦截功能
    # age
    # ('china', 'china', 'tom', 19)

    1. 可以看到我们重写了__getattribute__,并返回了object的该方法

    2. 类属性没有经过属性拦截器,实例属性经过属性拦截器,因为这是实例方法  (Student.country没有输出“开始属性校验...”)

    3. __getattribute__可以被重写,从而实现定制的返回

    重写属性拦截器

    class Student(object):
        country = "china"  
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
            print("开始属性校验拦截功能")
            print(attr)
            if attr == "name":  #注意这里引用原属性名不用self,直接引号引起来即可。
                print("现在开始调用的是name属性")
            elif attr =="age":
                print("age属性将被更改")
                self.age = 1000
                return 5555             # 如果这里没有return,会在后面那个return时返回1000
            else:
                print("现在调用的是其他属性")
            return object.__getattribute__(self, attr) #返回属性名
    
    s2 = Student("tom",19)
    print(s2.name,s2.age,s2.country)
    
    # 开始属性校验拦截功能
    # name
    # 现在开始调用的是name属性
    # 开始属性校验拦截功能
    # age
    # age属性将被更改
    # 开始属性校验拦截功能
    # country
    # 现在调用的是其他属性
    # ('tom', 1000, 'china')

    可以看到age属性被更改。

    __getitem__ and __setitem__

    如果在类中定义了上述方法,那么该类的实例可以这样取值 c[key];如果类的实例执行c[key]运算,就会调用__getitem__方法。

    class S(object):
        def __init__(self):
            self.s = 3
    
        def __getitem__(self, item):
            return self.s
    
        def __setitem__(self, key, value):
            setattr(self, key ,value)
    
    s = S()
    print s['a']        # 3
    s['s'] = 100
    print(s['s'])       # 100

    注意,这里写的比较简单,在 getitem 时,不管 item 是什么,都返回 self.s ,所以 s['a'] 返回了3,重点是理解用法。 

    __slots__ 槽

    __str__ and __repr__

    __str__ 用于显示类的真实内容,多用于类的 print

    如果没有 __str__

    class Student(object):
        def __init__(self, name):
            self.name = name
    print Student('Michael')        # <__main__.Student object at 0x109afb190>
    # 打印出一堆<__main__.Student object at 0x109afb190>,不好看

    如果定义了 __str__

    class Student(object):
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return 'Student object (name: %s)' % self.name
    print Student('Michael')        # Student object (name: Michael)

    显示的内容很直观了

    但是我们平常不这么用啊,print class,很少这么用,那怎么办呢

    s = Student('Michael')
    s       # <__main__.Student object at 0x109afb310>

    没有 print,内容还是不好看

    直接显示变量调用的不是 __str__,而是 _repr___;

    二者区别在于

    __str__ 返回用户看到的字符串;

    __repr__() 返回程序开发者看到的字符串,多用于调试

    解决办法是再定义一个__repr__()。但是通常 __str__() 和 __repr__() 代码都是一样的,所以,有个偷懒的写法

    class Student(object):
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return 'Student object (name=%s)' % self.name
        __repr__ = __str__

    未完待续...

  • 相关阅读:
    算法练习(16)-水平翻转一颗二叉树
    算法练习(15)-设计1个二叉树的序列化与反序列化实现?
    算法练习(14)-二叉树中2个节点的最近公共祖先?
    算法练习(13)-打印纸条对折的折痕类型(凹痕?凸痕?)
    算法练习(12)-二叉树的递归套路
    算法练习(11)-二叉树的各种遍历
    算法练习(10)-求2个(可能有环的)单链表的相交节点
    算法练习(9)-复杂带随机指针的单链表
    mac升级后第三方下载程序无法打开cannot be opened because the developer cannot be verified的解决办法
    算法练习(8)-判断单链表是否回文链表
  • 原文地址:https://www.cnblogs.com/yanshw/p/10700538.html
Copyright © 2011-2022 走看看