【第十二篇】Python面向对象进阶

风格不统一 提交于 2019-11-29 23:50:17

一、isinstance(obj,cls)和issubclass(sub, super)

1.1 isinstance

isinstance(obj,cls)检查obj是否是类cls的对象   包括继承关系

1 class Foo(object):
2     pass
3 
4 obj = Foo()
5 print(isinstance(obj, Foo)) # True

1.2 issubclass

issubclass(sub, super)检查sub类是否是super类的派生类   判断类与类之间的继承关系

1 class Foo(object):
2     pass
3 
4 
5 class Bar(Foo):
6     pass
7 
8 
9 print(issubclass(Bar, Foo))     # True

 二、特殊方法

跟运算符无关的特殊方法:

类别 方法名
字符串 / 字节序列表示形式 __repr__、 __str__、 __format__、 __bytes__
数值转换 __abs__、 __bool__、 __complex__、 __int__、 __float__、 __hash__、 __index__
集合模拟 __len__、 __getitem__、 __setitem__、 __delitem__、__contains__
迭代枚举 __iter__、 __reversed__、 __next__
可调用模拟 __call__
上下文管理 __enter__、 __exit__
实例创建和销毁 __new__、 __init__、 __del__
属性管理 __getattr__、 __getattribute__、 __setattr__、 __delattr__、 __dir__
属性描述符 __get__、 __set__、 __delete__
跟类相关的服务 __prepare__、 __instancecheck__、 __subclasscheck__
 
 跟运算符相关的特殊方法:
类名 方法名和对应的运算符
一元运算符 __neg__ -、 __pos__ +、 __abs__ abs()
众多比较运算符 __lt__ <、 __le__ <=、 __eq__ ==、 __ne__ !=、 __gt__ >、 __ge__ >=
算术运算符 __add__ +、 __sub__ -、 __mul__ *、 __truediv__ /、 __floordiv__ //、 __mod__ %、 __divmod__
反向算术运算符 __radd__、 __rsub__、 __rmul__、 __rtruediv__、 __rfloordiv__、 __rmod__、 __rdivmod__、 __rpow__
增量赋值算术运算符 __iadd__、 __isub__、 __imul__、 __itruediv__、 __ifloordiv__、 __imod__、 __ipow__
位运算符 __invert__ ~、 __lshift__ <<、 __rshift__ >>、 __and__ &、 __or__ |、 __xor__ ^
反向位运算符 __rlshift__、 __rrshift__、 __rand__、 __rxor__、 __ror__
增量赋值位运算符 __ilshift__、 __irshift__、 __iand__、 __ixor__、 __ior__
 
 

2.1 __setattr__, __delattr__, __getattr__

  • __setattr__  添加/修改属性会触发它的执行
  • __delattr__  删除属性的时候会触发
  • __getattr__  只有在调用属性且属性不存在的时候才会触发
 1 # 三者的用法演示
 2 class Foo:
 3     x = 1
 4     def __init__(self, y):
 5         self.y = y
 6 
 7     def __getattr__(self, item):
 8         print('----> from getattr:你找的属性不存在')
 9 
10     def __setattr__(self, key, value):
11         print('----> from setattr')
12         # self.key=value # 这就无限递归了,你好好想想
13         self.__dict__[key] = value  # 应该使用它
14 
15     def __delattr__(self, item):
16         print('----> from delattr')
17         # del self.item #无限递归了
18         self.__dict__.pop(item)
19 
20 # __setattr__  添加/修改属性会触发它的执行
21 f1=Foo(10)
22 print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
23 f1.z=3
24 print(f1.__dict__)
25 
26 # __delattr__    删除属性的时候会触发
27 f1.__dict__['a']=3    # 我们可以直接修改属性字典,来完成添加/修改属性的操作
28 del f1.a
29 print(f1.__dict__)
30 
31 # __getattr__    只有在使用点调用属性且属性不存在的时候才会触发
32 f1.xxxxxx

2.2 __getattribute__

__getattribute__ : 不管有没有属性都会执行到,当__getattribute__和__getattr__同时存在时,只有当__getattribute__抛出AttributeError的异常时,才会执行到__getattr__,否则会一直执行__getattribute__

 1 class Foo(object):
 2 
 3     def __init__(self, x):
 4         self.x = x
 5 
 6     def __getattr__(self, item):
 7         print("执行的是__getattr__")
 8 
 9     def __getattribute__(self, item):
10         print("执行的是getattribute")
11         raise AttributeError("抛出异常了")    # 只有抛出AttributeError的异常时,找不到属性才会执行__getattr__方法
12 
13 
14 f1 = Foo(10)
15 f1.x
16 f1.xxx

2.3 __getitem__、__setitem__、__delitem__

  • __getitem__ 以字典形式获取属性时,触发__getitem__
  • __setitem__ 以字典形式设置属性时,触发__setitem__
  • __delitem__ 删除字典的key值时,会触发__delitem__
 1 class Foo(object):
 2 
 3     def __getitem__(self, item):
 4         print("getitem----->%s" % item)
 5         return self.__dict__.get(item)
 6 
 7     def __setitem__(self, key, value):
 8         print("setitem---->%s %s" % (key, value))
 9         self.__dict__[key] = value
10 
11     def __delitem__(self, key):
12         print("delitem----->%s" % key)
13         self.__dict__.pop(key)
14 
15 
16 f1 = Foo()
17 print(f1.__dict__)
18 
19 f1['name'] = 'zhangsan'
20 print(f1.__dict__)
21 
22 print(f1['name'])
23 
24 del f1['name']
25 
26 print(f1.__dict__)

2.4 __str__、__repr__、__format__

    __str__与__repr__都能改变对象的字符串显示,需要注意的是:
    str函数或者print函数--->obj.__str__()
    repr或者交互式解释器--->obj.__repr__()
    如果__str__没有被定义,那么就会使用__repr__来代替输出
    注意:这俩方法的返回值必须是字符串,否则抛出异常
 1 class School(object):
 2 
 3     def __init__(self, name, age):
 4         self.name = name
 5         self.age = age
 6 
 7     def __str__(self):
 8         return '名字是%s,年龄是%s' % (self.name, self.age)
 9 
10     def __repr__(self):     # 只有当__str__没有定义时,才会执行
11         return 'aaa'
12 
13 
14 f1 = School('alex', '23')
15 print(f1)
16 
17 # 在子类中使用__str__,先找子类的__str__,没有的话向上找,只要父类不是object,就执行父类的__str__
18 # 但是如果除了object之外的父类都没有__str__方法,就执行子类的__repr__方法,如果子类也没有,还要向上找父类的__repr__方法
19 # 一直找不到,再执行object类中的__str__方法

__format__    格式化对象的字符串格式:

 1 date_dic = {
 2     'ymd': '{0.year}:{0.month}:{0.day}',
 3     'dmy': '{0.day}/{0.month}/{0.year}',
 4     'mdy': '{0.month}-{0.day}-{0.year}',
 5 }
 6 
 7 
 8 class Date(object):
 9     def __init__(self, year, month, day):
10         self.year = year
11         self.month = month
12         self.day = day
13 
14     def __format__(self, format_spec):
15         if not format_spec or format_spec not in date_dic:
16             format_spec = 'ymd'
17         fmt = date_dic[format_spec]
18         return fmt.format(self)
19 
20 
21 d1 = Date(2016, 12, 29)
22 print(format(d1))
23 print('{:mdy}'.format(d1))
24 print(format(d1, 'ymd'))

2.5 __slots__

1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__,当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
 1 class Foo(object):
 2     # 创建完后,该类就不具有__dict__属性
 3     __slots__ = ['name', 'age']
 4 
 5 
 6 f1 = Foo()
 7 f1.name = 'alex'
 8 f1.age = 18
 9 print(f1.name)
10 
11 f1.gender = 'male'  # 会报错 'Foo' object has no attribute 'gender'

2.6 __doc__

该属性的无法被继承

 1 class Foo:
 2     """
 3     我是描述信息
 4     """
 5     pass
 6 
 7 
 8 class Bar(Foo):
 9     pass
10 
11 
12 print(Foo.__doc__)  # 我是描述信息
13 print(Bar.__doc__)  # None

2.7 __module__和__class__

__module__ 表示当前操作的对象在那个模块
__class__     表示当前操作的对象的类是什么
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 
4 class C:
5 
6     def __init__(self):
7         self.name = ‘SB'
8 
9 # lib/aa.py
1 from lib.aa import C
2 
3 obj = C()
4 print(obj.__module__)  # 输出 lib.aa,即:输出模块
5 print(obj.__class__)      #  输出 lib.aa.C,即:输出类

2.8 __del__

析构方法,当对象在内存中被释放时,自动触发执行。
 
注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到__del__
 1 class Foo(object):
 2 
 3     def __init__(self, name):
 4         self.name = name
 5 
 6     def __del__(self):
 7         print("我被删除了")
 8 
 9 
10 f1 = Foo('alex')
11 del f1

2.9 __call__

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

    def __call__(self, *args, **kwargs):
        print("hello world")


f1 = Foo()

f1()

Foo()()

2.10 描述符(__get__、__set__、__delete__)

1.描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。
  __get__():调用一个属性时,触发
  __set__():为一个属性赋值时,触发
  __delete__():采用del删除属性时,触发
 
定义描述符:
class Foo(object):     #    在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

2.描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中

 1 class Foo(object):
 2 
 3     def __get__(self, instance, owner):
 4         print("get方法")
 5 
 6     def __set__(self, instance, value):
 7         print("set方法")
 8         # instance.__dict__['x'] = value
 9 
10     def __delete__(self, instance):
11         print("delete方法")
12 
13 
14 class Bar(object):
15 
16     x = Foo()
17 
18 
19 b1 = Bar()
20 b1.x = 1    # 触发__set__方法的执行
21 print(b1.__dict__)  # dict中为空
22 b1.x   # 触发__get__方法的执行
23 del b1.x    # 触发__delete__方法的执行
3.描述符分为两种:
  • 数据描述符:至少实现了__get__()和__set__()
  • 非数据描述符:没有实现__set__()
4.注意事项:
  • 描述符本身应该定义成新式类,被代理的类也应该是新式类
  • 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
  • 要严格遵循该优先级,优先级由高到底分别是
    • 类属性
    • 数据描述符
    • 实例属性
    • 非数据描述符
    • 找不到的属性触发__getattr__()

类属性>数据描述符

 1 # 描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str调用')
 5     def __set__(self, instance, value):
 6         print('Str设置...')
 7     def __delete__(self, instance):
 8         print('Str删除...')
 9 
10 
11 class People:
12     name=Str()
13     def __init__(self,name,age): #name被Str类代理,age被Int类代理,
14         self.name=name
15         self.age=age
16 
17 
18 # 基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
19 
20 # 那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
21 People.name # 恩,调用类属性name,本质就是在调用描述符Str,触发了__get__()
22 
23 People.name='egon'  # 那赋值呢,我去,并没有触发__set__()
24 del People.name  # 赶紧试试del,我去,也没有触发__delete__()
25 #结论:描述符对类没有作用-------->傻逼到家的结论
26 
27 '''
28 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
29 People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
30 
31 People.name='egon' # 那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
32 del People.name #同上
33 '''

数据描述符>实例属性

 1 #描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str调用')
 5     def __set__(self, instance, value):
 6         print('Str设置...')
 7     def __delete__(self, instance):
 8         print('Str删除...')
 9 
10 
11 class People:
12     name=Str()
13     def __init__(self,name,age): #name被Str类代理,age被Int类代理,
14         self.name=name
15         self.age=age
16 
17 
18 p1=People('egon',18)
19 
20 
21 # 如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
22 p1.name='egonnnnnn'
23 p1.name
24 print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
25 del p1.name

实例属性 > 非数据描述符

 1 class Foo:
 2     def func(self):
 3         print('我胡汉三又回来了')
 4 
 5 f1 = Foo()
 6 f1.func() # 调用类的方法,也可以说是调用非数据描述符
 7 # 函数是一个非数据描述符对象(一切皆对象么)
 8 print(dir(Foo.func))
 9 print(hasattr(Foo.func,'__set__'))
10 print(hasattr(Foo.func,'__get__'))
11 print(hasattr(Foo.func,'__delete__'))
12 # 有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了
13 # 笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
14 # 函数就是一个由非描述符类实例化得到的对象
15 # 没错,字符串也一样
16 
17 f1.func='这是实例属性啊'
18 print(f1.func)
19 
20 
21 del f1.func # 删掉了非数据
22 f1.func()

再次验证:实例属性>非数据描述符

 1 class Foo:
 2     def __set__(self, instance, value):
 3         print('set')
 4     def __get__(self, instance, owner):
 5         print('get')
 6 class Room:
 7     name=Foo()
 8     def __init__(self,name,width,length):
 9         self.name=name
10         self.width=width
11         self.length=length
12 
13 
14 
15 
16 # name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
17 #对实例的属性操作,触发的都是描述符的
18 r1=Room('厕所',1,1)
19 r1.name
20 r1.name='厨房'
21 
22 
23 
24 class Foo:
25     def __get__(self, instance, owner):
26         print('get')
27 
28 class Room:
29     name=Foo()
30     def __init__(self,name,width,length):
31         self.name=name
32         self.width=width
33         self.length=length
34 
35 
36 
37 # name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
38 # 对实例的属性操作,触发的都是实例自己的
39 r1=Room('厕所',1,1)
40 r1.name
41 r1.name='厨房'

非数据描述符>找不到

 1 class Foo:
 2     def func(self):
 3         print('我胡汉三又回来了')
 4 
 5 
 6     def __getattr__(self, item):
 7         print('找不到了当然是来找我啦',item)
 8 
 9 
10 f1=Foo()
11 f1.xxxxxxxxxxx    # 找不到,调用__getattr__方法
 
5.描述符的使用
    众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能
 1 class Typed(object):
 2     def __init__(self,name,expected_type):
 3         self.name=name
 4         self.expected_type=expected_type
 5     
 6     def __get__(self, instance, owner):
 7         print('get--->',instance,owner)
 8         if instance is None:
 9             return self
10         return instance.__dict__[self.name]
11 
12     def __set__(self, instance, value):
13         print('set--->',instance,value)
14         if not isinstance(value,self.expected_type):
15             raise TypeError('Expected %s' %str(self.expected_type))
16         instance.__dict__[self.name]=value
17     
18     def __delete__(self, instance):
19         print('delete--->',instance)
20         instance.__dict__.pop(self.name)
21 
22 
23 class People(object):
24     name=Typed('name',str)
25     age=Typed('name',int)
26     salary=Typed('name',float)
27     def __init__(self,name,age,salary):
28         self.name=name
29         self.age=age
30         self.salary=salary
31 
32 p1=People(123,18,3333.3)
33 p1=People('egon','18',3333.3)
34 p1=People('egon',18,3333)

6.类装饰器

 1 # 检查对象属性的类型
 2 class Typed(object):
 3     def __init__(self, name, expected_type):
 4         self.name = name
 5         self.expected_type = expected_type
 6     
 7     def __get__(self, instance, owner):
 8         print('get--->', instance, owner)
 9         if instance is None:
10             return self
11         return instance.__dict__[self.name]
12 
13     def __set__(self, instance, value):
14         print('set--->', instance, value)
15         if not isinstance(value,self.expected_type):
16             raise TypeError('Expected %s' % str(self.expected_type))
17         instance.__dict__[self.name] = value
18         
19     def __delete__(self, instance):
20         print('delete--->', instance)
21         instance.__dict__.pop(self.name)
22 
23 
24 def typeassert(**kwargs):
25     def decorate(cls):
26         print('类的装饰器开始运行啦------>',kwargs)
27         for name,expected_type in kwargs.items():
28             setattr(cls,name,Typed(name,expected_type))
29         return cls
30     return decorate
31 
32 # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
33 @typeassert(name=str,age=int,salary=float)  
34 class People:
35     def __init__(self,name,age,salary):
36         self.name=name
37         self.age=age
38         self.salary=salary
39 
40 
41 print(People.__dict__)
42 p1 = People('egon', 18, 3333.3)
7.描述符总结
    描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性.
    描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
8.描述符的案例
    利用描述符,实现自定制的property,计算房屋面积
 1 class LazyProperty(object):
 2 
 3     def __init__(self, func):
 4         self.func = func
 5 
 6     def __get__(self, instance, owner):
 7         # print(self.func)
 8         # print(instance)
 9         # print(owner)
10         # print("haha")
11         return self.func(instance)
12 
13 
14 class Room(object):
15 
16     def __init__(self, name, width, length):
17         self.name = name
18         self.width = width
19         self.length = length
20 
21     @LazyProperty        # 相当于 area = LazyProperty(area)
22     def area(self):
23         return self.width * self.length
24 
25 
26 r1 = Room("卧室", 2, 3)
27 print(r1.area)

实现缓存计算的功能

 1 class Lazyproperty:
 2     def __init__(self,func):
 3         self.func=func
 4 
 5     def __get__(self, instance, owner):
 6         print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
 7         if instance is None:
 8             return self
 9         else:
10             print('--->')
11             value = self.func(instance)
12             setattr(instance, self.func.__name__,value) #计算一次就缓存到实例的属性字典中
13             return value
14 
15 class Room:
16     def __init__(self,name,width,length):
17         self.name=name
18         self.width=width
19         self.length=length
20 
21     @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
22     def area(self):
23         return self.width * self.length
24 
25 r1=Room('alex',1,1)
26 print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
27 print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算

做了一点小改动,缓存计算的功能就失效了

 1 # 缓存不起来了
 2 
 3 class Lazyproperty:
 4     def __init__(self,func):
 5         self.func=func
 6 
 7     def __get__(self, instance, owner):
 8         print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
 9         if instance is None:
10             return self
11         else:
12             value=self.func(instance)
13             instance.__dict__[self.func.__name__]=value
14             return value
15         # return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情
16 
17     def __set__(self, instance, value):
18         print('hahahahahah')
19 
20 class Room:
21     def __init__(self,name,width,length):
22         self.name=name
23         self.width=width
24         self.length=length
25 
26     @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
27     def area(self):
28         return self.width * self.length
29 
30 print(Room.__dict__)
31 r1=Room('alex',1,1)
32 print(r1.area)
33 print(r1.area)
34 print(r1.area)
35 print(r1.area) # 缓存功能失效,每次都去找描述符了,为何,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了

2.11 上下文管理协议(__enter__和__exit__)

上下文管理协议: 即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

 1 # 上下文管理协议
 2 class Open:
 3     def __init__(self,name):
 4         self.name=name
 5 
 6     def __enter__(self):
 7         print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
 8         # return self
 9 
10     def __exit__(self, exc_type, exc_val, exc_tb):
11         print('with中代码块执行完毕时执行我啊')
12 
13 
14 # with Open--》触发Open.__enter__,拿到返回值
15 # as f ---> f = __enter__的返回值
16 # with Open('a.txt') as f =======> f = Open('a.txt')
17 with Open('a.txt') as f:
18     print('=====>执行代码块')
19     # print(f,f.name)

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

 1 class Open:
 2     def __init__(self,name):
 3         self.name=name
 4 
 5     def __enter__(self):
 6         print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
 7 
 8     def __exit__(self, exc_type, exc_val, exc_tb):
 9         print('with中代码块执行完毕时执行我啊')
10         print(exc_type)
11         print(exc_val)
12         print(exc_tb)
13 
14 
15 
16 with Open('a.txt') as f:
17     print('=====>执行代码块')
18     raise AttributeError('***着火啦,救火啊***')
19 print('0'*100) #------------------------------->不会执行

如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

 1 class Open(object):
 2 
 3     def __init__(self, name):
 4         self.name = name
 5 
 6     def __enter__(self):
 7         print("执行enter")
 8         return self
 9 
10     def __exit__(self, exc_type, exc_val, exc_tb):
11         print("执行exit")
12         print(exc_type)
13         print(exc_val)
14         print(exc_tb)
15         return True     # 包容了with中的异常,返回true,会正常结束with语句,会继续执行with后的语句;如果不包容异常,则出现异常就会停止
16 
17 
18 with Open('aa') as f:
19     print(f)
20     raise Exception("aa")
21 
22 print("bbb")
__exit__语句的执行结束,表示with语句的执行结束
 
使用上下文管理协议的好处:
1. 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2. 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

2.12 __dict__

类或对象中的所有成员
我们知道:类的普通字段属于对象;类中的静态字段和方法等属于类,即:
 1 class Province:
 2 
 3     country = 'China'
 4 
 5     def __init__(self, name, count):
 6         self.name = name
 7         self.count = count
 8 
 9     def func(self, *args, **kwargs):
10         print 'func'
11 
12 
13 # 获取类的成员,即:静态字段、方法、
14 print Province.__dict__
15 # 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}
16 
17 
18 obj1 = Province('HeBei',10000)
19 print obj1.__dict__
20 # 获取 对象obj1 的成员
21 # 输出:{'count': 10000, 'name': 'HeBei'}
22 
23 
24 obj2 = Province('HeNan', 3888)
25 print obj2.__dict__
26 # 获取 对象obj1 的成员
27 # 输出:{'count': 3888, 'name': 'HeNan'}

2.13 __new__ 构造方法

 __init__ 初始化方法

 1 class Single(object):
 2 
 3     __instance = None
 4 
 5     def __new__(cls, *args, **kwargs):
 6         if cls.__instance is None:
 7             cls.__instance = object.__new__(cls)
 8         return cls.__instance
 9 
10 
11     def __init__(self):
12         print("init")
13 
14 
15 # 开辟一个空间,属于对象的
16 # 把对象的空间传给self,执行init
17 # 将这个对象的空间返回给调用者
18 obj = Single()
19 obj2 = Single()
20 print(id(obj), id(obj2))
21 
22 
23 # 单例:如果一个类,从头到尾只能有一个实例,那么这个类就是一个单例类

2.14 __len__

 1 class A:
 2     def __init__(self):
 3         self.a = 1
 4         self.b = 2
 5 
 6 
 7     def __len__(self):
 8         return len(self.__dict__)
 9 a = A()
10 print(len(a))

2.15 __hash__

 1 class A:
 2     def __init__(self):
 3         self.a = 1
 4         self.b = 2
 5 
 6 
 7     def __hash__(self):
 8         return hash(str(self.a)+str(self.b))
 9 a = A()
10 print(hash(a))

2.16 __eq__

 1 class A:
 2     def __init__(self):
 3         self.a = 1
 4         self.b = 2
 5 
 6 
 7     def __eq__(self,obj):
 8         if  self.a == obj.a and self.b == obj.b:
 9             return True
10 a = A()
11 b = A()
12 print(a == b)

2.17 __bool__

bool(x)  的背后,调用的就是__bool__()方法. 如果不存在 __bool__ 方法, 那么 bool(x) 会尝试调用 x.__len__()。 若返回 0, 则 bool 会返回False; 否则返回 True。

from math import hypot




class Vector:
    """
    二维向量
    """


    def __init__(self, x, y):
        self.x = x
        self.y = y


    def __repr__(self):
        return 'Vector(%s, %s)' % (self.x, self.y)


    def __abs__(self):
        return hypot(self.x, self.y)


    def __bool__(self):
        return bool(abs(self))


    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Vector(x, y)


    def __mul__(self, other):
        return Vector(self.x * other, self.y * other)

三、加工数据类型

包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)

 1 class List(list):
 2 
 3     def show_middle(self):
 4         mid_index = int(len(self) / 2)
 5         return self[mid_index]
 6 
 7     # 对append方法进行重写,只允许添加字符串类型
 8     def append(self, p_object):
 9         if type(p_object) is str:
10             super(List, self).append(p_object)
11         else:
12             print('只能添加字符串类型')
13 
14 
15 l1 = List("hello world")
16 print(type(l1))
17 print(l1.show_middle())
18 
19 l1.append("aa")
20 print(l1)
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
 
实现授权的关键点就是覆盖__getattr__方法
 
文件读写实现授权:
 1 class FileHandle(object):
 2 
 3     def __init__(self, filename, mode='r', encoding='utf-8'):
 4         # self.filename = filename
 5         self.file = open(filename, mode, encoding=encoding)
 6         self.mode = mode
 7         self.encoding = encoding
 8 
 9     def write(self, data):
10         t = time.strftime('%Y-%m-%d %X')
11         self.file.write('%s %s' % (t, data))
12 
13     def __getattr__(self, item):
14         return getattr(self.file, item)
15 
16 
17 f1 = FileHandle('a.txt', 'w')
18 print(f1.file)
19 # print(f1.read)
20 f1.write("aabbcc")
21 f1.close()
文件读写加上b模式:
 1 #_*_coding:utf-8_*_
 2 
 3 #我们来加上b模式支持
 4 import time
 5 class FileHandle:
 6     def __init__(self,filename,mode='r',encoding='utf-8'):
 7         if 'b' in mode:
 8             self.file=open(filename,mode)
 9         else:
10             self.file=open(filename,mode,encoding=encoding)
11         self.filename=filename
12         self.mode=mode
13         self.encoding=encoding
14 
15     def write(self,line):
16         if 'b' in self.mode:
17             if not isinstance(line,bytes):
18                 raise TypeError('must be bytes')
19         self.file.write(line)
20 
21     def __getattr__(self, item):
22         return getattr(self.file,item)
23 
24     def __str__(self):
25         if 'b' in self.mode:
26             res="<_io.BufferedReader name='%s'>" %self.filename
27         else:
28             res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding)
29         return res
30 
31 
32 f1=FileHandle('b.txt','wb')
33 # f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气
34 f1.write('你好啊'.encode('utf-8'))
35 print(f1)
36 f1.close()

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!