python面向对象编程(5)

来源:互联网 发布:php 随机昵称生成器 编辑:程序博客网 时间:2024/06/14 16:56

python类重载运算符

1.成员操作符:__contains__

判断member是否存在可迭代的对象

# membership.py# 定义的对象必须是可迭代的class Iters:"""   将所有可迭代的方法列出来 """    def __init__(self, value):        self.data = value    def __getitem__(self, i):    # 迭代、索引、分片操作的时候会被调用        print('get[%s]:' % i, end='')        return self.data[i]    def __iter__(self):          # 进行迭代的时候会被调用        print('iter=> ', end='')        self.ix = 0        return self    def __next__(self):        print('next:', end='')        if self.ix == len(self.data):raise StopIteration        item = self.data[self.ix]        self.ix += 1        return item    def __contains__(self, x):      # 成员变量的判断,使用in        print('contains: ', end='')        return x in self.data    next = __next__def test_iter():    X = Iters([1,2,3,4])    print(3 in X)   # contains: True    for i in X:        print(i, end=' | ')if __name__ == '__main__':    test_iter()>>> python membership.py

这里写图片描述

2.属性管理

属性访问__getattr__

  • obj.attr_name的形式访问对象属性
  • 自定义类管理属性信息,对于访问不存在的属性可以返回错误信息提示
# attr.pyclass AttrObject:    def __getattr__(self, attr_name):        print("Attr Object get attr method ....")        if attr_name == 'age':            return 40        else:            raise AttributeError("could not access the attr[%s]" % attr_name)# for testdef test_empty():    e = AttrObject()    print(e.age)        # 调用__getattr__方法,访问age属性,匹配并返回40    print(e.hobby)      # AttributeError: could not access the attr[hobby]if __name__ == '__main__':    test_empty()>>> python attr.py      # 2.x & 3.x表示结果一样,以下都会以此标记

这里写图片描述

属性设置__setattr__

  • 定义类对象管理属性并将属性名以及值存储在一个实例对象中
  • self.attrname 以及 instance.attrname 将会调用类的内置方法__setattr__方法
# attr.pyclass AcessControl:    def __init__(self):        self.hobby = "basketball"               # 会调用下面的__setattr__方法    def __setattr__(self, key, value):        # self.name = "xxxx"                    # 不能在__setattr__上使用self.attr,会导致递归应用循环        print("access control set attr ...")        if key == 'age':            self.__dict__[key] = value + 10     # 通过内建字典来保存属性数据        else:            self.__dict__[key] = value    def __delattr__(self, item):        print("del item[%s]" % item)    def __getattr__(self, item):        print("get item[%s]" % item)def test_access_control():    ac = AcessControl()    ac.age = 10         # 调用__setattr__    print(ac.age)       # 直接输入值,没有调用__getattr__    print(ac.hobby)     # 当属性有值时,也就是非None是不会调用__getattr__方法的,如果没有值,即None就会调用__getattr__方法    del ac.age          # 调用__delattr__    print(ac.name)      # 调用__getattr__,调用未定义的属性时候就会回调这个函数并且返回Noneif __name__ == '__main__':    test_access_control()>>> python attr.py      # 2.x & 3.x

这里写图片描述

属性设置与访问__getattribute__

  • 这个方法将会拦截所有获取的属性的操作,包括未定义的属性
  • 属性内置函数允许我们将方法与特定类属性的获取和集合操作相关联
  • 属性描述符为特定的类的属性提供了一组__get____set__方法访问协议
  • 实例属性在类中声明但是在每一个类的对象实例中隐式存储
class AttrManager:    def __getattribute__(self, item):        """        :param item:        :return:        避免产生递归循环引用,不要使用self.attr_name对属性进行访问或者设置,属性访问会经过这个方法        这里没有保存数据,因此返回的时候是None        """        print("call  __getattribute__ for item[%s] " % item)def test_attr_manager():    manager = AttrManager()    manager.name = "attr manager name"    print(manager.name)    print(manager.age)if __name__ == '__main__':    test_attr_manager()>>> python attr.py  # 2.x与3.x的不同输出

这里写图片描述
这里写图片描述

小结

  • __setattr____getattr__能够在python不同的版本下对未定义的属性进行访问和赋值
  • __getattribute__只能在3.x下对未定义的属性名进行访问
  • 为了更好的兼容2.x和3.x,应当重载两个方法,即__getattr__以及__getattribute__
  • python3.x中的执行的优先级,__getattribute__ > __getattr__
  • python2.x中的执行的优先级,__getattr__ > __getattribute__
  • 如果类中没有定义__getattribute__或者__getattr__,则在访问未定义的属性时候会报没有属性的错误

3.字符串打印:__str__,__repr__

  • __str__主要应用于print函数以及字符串函数str的转换操作,
  • __repr__应用于所有输出操作,如果有print以及str操作并定义__str__,则会以__str__为准
  • __repr____str__均未定义的时候,默认打印的是输出对象地址信息
# str.pyclass DisplayClass:    """    __repr__ is used everywhere, except by print and str when a __str__ is defined.    __str__ to support print and str exclusively    """    def __repr__(self):        return "display __repr__ class"    def __str__(self):        return "display __str__ class"# 使用命令行的形式打印输出  2.x & 3.x 输出效果一致,以2.x作为截图>>> d = DisplayClass()>>> d                   repr>>> print(d)            str>>> print(repr(d))      repr>>> print(str(d))       str

这里写图片描述
4.运算操作符:__add__,__radd__,__iadd__

变量+常量,如X + 1024,调用__add__方法

# addition.pyclass Counter:    def __init__(self, value = 0):        self.data = value    def __add__(self, other):        print("counter data[%s] add [%d]..." % (self.data,other))        return self.data + otherdef test_add():    r = Counter()    q = r + 1024       # call add, instance +    print("q is %d" % q)test_add()>>> python addition.py

这里写图片描述

常量+变量,如1024 + X,调用__radd__方法

# addition.pyclass Counter:    ...     # 省略上述代码    ...    def __radd__(self, other):        print("counter data[%s] radd [%d]..." % (self.data,other))        return self.data + otherdef test_radd():    p = 7 + r       # call radd, + instance    print("the p is %d" % p)test_radd()>>> python addition.py

这里写图片描述

变量+变量,如X + Y

# addition.py# 执行X+Y的时候,会先执行__add__方法,再执行__radd__方法,此时代码需要稍微调整下class Counter:    def __radd__(self, other):        if isinstance(other,Counter):            print("counter data[%s] radd [%d]..." % (self.data,other.data))        elif isinstance(other,int):            print("counter data[%s] radd [%d]..." % (self.data, other))        return self.data + other    def __add__(self, other):        if isinstance(other,Counter):            print("counter data[%s] add [%d]..." % (self.data,other.data))        elif isinstance(other,int):            print("counter data[%s] add [%d]..." % (self.data, other))        return self.data + otherdef test_addition():    X = Counter(2)    Y = Counter(4)    sum = X + Y     # X + Y ==> 2 + Y ==> 4 + 2    print(sum)test_addition()>>> python addition.py

这里写图片描述

5.回调:__call__

函数回调设计

# others.py# 使用函数进行回调def callback_fn(color):    def oncall():        print("select the color[%s]" % color)    return oncalldef test_callback_fn():    fn = callback_fn("green")    fn()test_callback_fn()>>> python others.py

这里写图片描述

匿名函数回调

# others.pydef test_lamaba_callback():    cs4 = (lambda color='red': print('turn ' + color))    cs4()test_lamaba_callback()>>> python others.py

这里写图片描述

类回调设计

# others.pyclass CallBack:    def __init__(self,color):        self.color = color    def __call__(self):        print("call callback color[%s]" % self.color)class EventCall:    def __init__(self,callback = None):        self.c = callback    def press(self):        self.c()def test_call_back():    c1 = CallBack("red")    c2 = CallBack("green")    e1 = EventCall(callback = c1)    e2 = EventCall(callback = c2)    e1.press()      # when press then trigger callback    e2.press()test_call_back()>>> python others.py

这里写图片描述

6.比较运算符

  • py3.x不存在__cmp__以及cmp方法,也就是py3.x不能再使用__cmp__以及cmp方法,py2.x仍然保留
  • 为兼容性,统一在py2.x与py3.x使用__lt____gt__方法
# others.pyclass Comp:    """    方式一    """    data = 'spam'    def __gt__(self, other): return self.data > other    def __lt__(self, other): return self.data < otherdef test_comp():    c = C()    print(c > 'ham')    print(c < 'ham')test_comp()>>> python others.py 

这里写图片描述

py2.x可用而py3.x不可用情况

# others.pyclass S:    data = "spam"    """    python3.x将不可用    """    def __cmp__(self, other):        return cmp(self.data,other)class D:    """    方式三,改变cmp的方法,3.x仍然报错    """    data = "d"    def __cmp__(self, other):        return (self.data > other) - (self.data < other)def test_S():    s = S()    print(s > 'ham')def test_d():    d = D()    print(d > 'ham')    print(d < 'ham')test_S()test_d()>>> python others.py

这里写图片描述
7.布尔测试:__bool__,__len__,__nozero__

  • py3.x:如果没有__bool__就会执行__len__,也就是两个都存在的时候,__bool__的优先级比__len__
  • py2.x如果没有__bool__就会执行__len__,也就是两个都存在的时候,__len__的优先级比__bool__
# others.pyclass Truth:    def __bool__(self):        print("call bool method ...")        return True    def __len__(self):        print("call len method ....")        return 0def test_truth():    X = Truth()    if X:           print("i'm truth ....")test_truth()>>> python others.py

这里写图片描述
这里写图片描述

__nonzero__:仅在python2.x使用,用于返回一个布尔值,并且优先级大于上面的__len__方法

# others.pyclass Truth:    # 定义了__bool__方法以及__len__方法    ....    def __nonzero__(self):        print "call zero for python2.x"        return False... # 同样的测试代码>>> python others.py

这里写图片描述

8.析构函数:__del__

del使用笔记

  • Need:即是否必需,python会自动回收内存空间,析构器并非是空间管理的一项必需工具
  • Predictability:即是否可预测,python退出解释器的时候,并不能保证会调用仍然存在的对象的析构函数来进行回收操作
  • Exceptions:由于存在异常情况并且不知道异常发生的时刻,析构器很难确定什么时候调用来进行回收操作
  • Cycles:对象的循环引用可以防止垃圾回收器发生一些如我们所期望的错误

代码测试

# others.pyclass Life:    def __init__(self, name='unknown'):        print('Hello ' + name)        self.name = name    def live(self):        print(self.name)    def __del__(self):        print('Goodbye ' + self.name)def test_del():    l = Life("keithl")    l.live()if __name__ == '__main__':    test_del()>>> python others.py        # 2.x & 3.x

这里写图片描述

个人公众号

这里写图片描述