Python基础(八)面向对象02

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

 isinstance(obj,cls)检查是否obj是否是类 cls 的对象
 issubclass(sub, super)检查sub类是否是 super 类的派生类(子类)

class Animal():
    pass

class Dog(Animal):
    pass

d1 = Dog()
# 检查d1是不是Dog的一个对象
print(isinstance(d1, Dog))

print(issubclass(Dog, Animal))
print(issubclass(Animal, object))

# 运行结果
# True
# True
# True

反射 hasattr setattr getattr delattr

概念:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)

四个可以实现自省的函数:

这四个方法适用于对象和类(一切皆对象,类也是一种对象)

 1 class Dog():
 2     type = '犬科'
 3 
 4     def __init__(self, name, gender):
 5         self.name = name
 6         self.gender =gender
 7 
 8     def info(self):
 9         print('name is {}, gender is {}'.format(name, gender))
10 
11 d1 = Dog('小黄', '母')
12 
13 # 检测是否可以使用某属性(数据属性、方法属性)
14 print(hasattr(d1, 'type'))
15 
16 # 获取属性对应的属性值
17 print(getattr(d1, 'type'))
18 #print(getattr(d1, 'age')) #找不到 d1.age 报错
19 print(getattr(d1, 'age', '没有找到age')) #可以设置返回值,找不不报错
20 
21 # 设置属性
22 setattr(d1, 'type', '大黄')
23 print(getattr(d1, 'type'))
24 setattr(d1, 'add', lambda x:x+1) #也可以设置函数属性
25 print(d1.add) #<function <lambda> at 0x02EE07C8>
26 print(d1.add(3)) #4
27 
28 
29 # 删除属性
30 delattr(d1, 'type')
31 #delattr(d1, 'abc') #不存在,报错
32 
33 print(d1.__dict__)

动态导入模块

 1 # from mod import a
 2 
 3 # import mod.a
 4 
 5 # 使用__import__导入,拿到的是最顶级的模块
 6 m = __import__('mod.a')
 7 print(m) #<module 'mod' (namespace)>
 8 m.a.p()
 9 
10 # 使用importlib模块处理模块的导入操作时,拿到的时目标模块
11 import  importlib
12 m = importlib.import_module('mod.a')
13 print(m) #<module 'mod.a' from 'E:\\PycharmProjects\\untitled\\day26\\mod\\a.py'>
14 m.p()

 

__getattr__ __setattr__ __delattr__

# __getattr__ __setattr__ __delattr__是类的内置函数,dir()查看
class Dog():
    type = 'animal'
    def __init__(self, name):
        self.name = name

    # 实例使用点.调用不存在的属性时触发
    def __getattr__(self, item):
        print("from getattr 没有找到该属性")

    # 添加修改实例属性时触发
    def __setattr__(self, key, value):
        print('from setattr ')
        #self.key = value #这就无限递归了!
        #self.__dict__[key] = value #正确赋值方法

    # 删除实例属性时触发
    def __delattr__(self, item):
        print("from delattr")

d1 = Dog('小黄')
print(d1.__dict__)
#因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值

d1.type #可以访问到,没有触发__getattr__
d1.namessssss

d1.name = "大黄"
d1.__dict__['age'] = 18 #我们可以直接修改属性字典,来完成添加/修改属性的操作
print(d1.__dict__)

del d1.age

执行结果:

1 from setattr 
2 {}
3 from getattr 没有找到该属性
4 from setattr 
5 {'age': 18}
6 from delattr

包装和授权

包装:python为我们提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就是包装。也就是对基本数据类型进行二次加工

通过继承和派生实现包装

class List(list):
    def append(self, object):
        '派生自己的appdend:只允许追加int类型'
        if type(object) is int:
            super().append(object)
        else:
            print('只支持append整型int')

    @property
    def middle(self):
        """新增一个方法,用于获取列表的中间值"""
        i = int(len(self)/2)
        return self[i]

x = [1, 2, 3, 4]
print(type(x))

lx = List(x)
print(type(lx))

lx.append(5)
print(lx)
lx.append('a')

print(lx.middle)

# 运行结果
# <class 'list'>
# <class '__main__.List'>
# [1, 2, 3, 4, 5]
# 只支持append整型int
# 3

通过授权实现包装

授权:通过授权的方式实现包装

授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

 1 class List():
 2     def __init__(self, seq):
 3         self.seq = seq
 4 
 5     def append(self, obj):
 6         if type(obj) is int:
 7             self.l.append(obj)
 8         else:
 9             print("只能append整型int")
10 
11     def __getattr__(self, item):
12         return getattr(self.seq, item)
13 
14     def __str__(self):
15         return str(self.seq)
16 
17 i = List([1, 2])
18 i.append("a")
19 print(i)

 

__getattribute__

 1 class Dog():
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     def __getattr__(self, item): #当getattrbute抛出AttributeError异常时才执行
 6         print("这个玩意不存在from getattr")
 7 
 8     def __getattribute__(self, item): #实例寻找变量时都会触发
 9         print('无论是否存在都执行 from getattrbute')
10         raise AttributeError('这个玩意不存在,我要抛出一个异常')
11 
12 d1 = Dog('小黄') #触发__getattribute__,但是这里没有成功赋值
13 d1.name
14 d1.age

__setitem__,__getitem,__delitem__

在使用实例字典进行操作时会触发 __setitem__   __getitem__  __delitem__

在使用实例加点对变量进行操作时会触发 __setattr__  __getattr__  __delattr__

 1 class Dog():
 2     def __getitem__(self, item):
 3         print('from getitem')
 4         # return self.__dict__[item] #返回操作
 5 
 6     def __setitem__(self, key, value):
 7         print('from setitem')
 8         #self.__dict__[key] = value 赋值操作
 9     def __delitem__(self, key):
10         print('from delitem')
11         #self.__dict__.pop(key) #删除操作
12 
13 d1= Dog()
14 d1.age = 11 #使用实例名加点 . 的方式会触发getattr,不会触发getitem
15 print(d1.__dict__)
16 
17 del d1['age'] #使用字典的方式,会触发delitem
18 d1['name'] = '小黄'
19 print(d1['name'])

_str__,__repr__,__format__

常见的对象显示字符串的格式:

l = [1, 2, 3]
print(l)

class Dog():
    pass

d1 = Dog()
print(d1)

f = open('test.txt', 'w')
print(f)

# 运行结果如下
# [1, 2, 3]
# <__main__.Dog object at 0x00CA9C30>
# <_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'>
View Code

 

_str__,__repr__,__format__必须有返回值,且返回值只能是字符串,否则抛异常

  print()和str()都会触发obj.__str__()

  repr()或者交互解释器中都会触发obj.__repr__()

  如果str没有定义,则使用repr来代替输出

str和repr

class Dog():
    def __init__(self, name):
        self.name = name

    def __str__(self):
        print('from __str__')
        return ('这条狗的名字是%s' %self.name)

    def __repr__(self):
        return ('from repr: %s' %self.name)

d1 = Dog('小黄')
print(d1) #print()和str()都会触发obj.__str__()
str(d1)

print(repr(d1)) #repr()或者交互解释器中都会触发obj.__repr__()

# str repr的必须有返回值,返回值必须是字符串,否则跑异常
# 如果str没有定义,则使用repr来代替输出

# 运行结果
# from __str__
# 这条狗的名字是小黄
# from __str__
# from repr: 小黄

 

 format

class Dog():
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender =gender
    def __format__(self, format_spec):
        return 'from format'


d1 = Dog('小黄', 18, '')
print(format(d1)) #format()触发__format__()
print('{}'.format(d1)) #触发__format()

# 运行结果
# from format
# from format
format_dict = {
    'nag': 'name:{0.name}, age:{0.age}, gender:{0.gender}',
    'agn': 'age:{0.age}, gender:{0.gender}, name:{0.name}',
    'gna': 'gender:{0.gender}, name:{0.name}, age:{0.age}',
}

class Dog():
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender =gender
    def __format__(self, format_spec):
        if not format_spec or format_spec not in format_dict:
            format_spec = 'nag'
        i = format_dict[format_spec]
        return i.format(self)

d1 = Dog('小黄', 18, '')

print(format(d1, 'agn'))
print(format(d1, 'dasjgi sd h'))

# 运行结果
# age:18, gender:公, name:小黄
# name:小黄, age:18, gender:公

 

__slots__

__slots__取代了实例的__dict__字典

当一个类中变量很少,但是这个类的实例很多时,每一个实例都有一个属性字典会占用大量内存,此时__slots__可以帮助节省内存,但是属性字典的相关操作就限制了

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

'''
class Foo:
    __slots__='x'


f1=Foo()
f1.x=1
f1.y=2#报错
print(f1.__slots__) #f1不再有__dict__

class Bar:
    __slots__=['x','y']
    
n=Bar()
n.x,n.y=1,2
n.z=3#报错

 

class Foo:
    __slots__=['name','age']

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
#f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

 

__doc__

可以查看一个类的文档注释,无法被继承

__module__和__class__

__module__ 表示当前操作的对象在那个模块

__class__     表示当前操作的对象的类是什么

 __del__

析构方法,当对象在内存中被释放时,自动触发执行。

当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

#输出结果
执行我啦
------->

 

 __call__

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

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

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__

 

__next__和__iter__实现迭代器协议

实现斐波那契数列

class Fb():
    def __init__(self):
        self.x = 0
        self.y =1
    def __iter__(self):
        return self
    def __next__(self):
        if self.y > 100:
            raise StopIteration("大于100,停止")
        self.x, self.y = self.y, self.x+self.y
        return self.x

f1 = Fb()
print(next(f1))
print(next(f1))
print('===>')
for i in f1: # iter(f1) -->  __iter__()  __next__()
    print(i)

 

描述符(__get__,__set__,__delete__)

概念 

描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
  __get__():调用一个属性时,触发
  __set__():为一个属性赋值时,触发
  __delete__():采用del删除属性时,触发

定义描述符:

1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
2     def __get__(self, instance, owner):
3         pass
4     def __set__(self, instance, value):
5         pass
6     def __delete__(self, instance):
7         pass

作用

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

描述符产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法

 1 class Foo:
 2     def __get__(self, instance, owner):
 3         print('触发get')
 4     def __set__(self, instance, value):
 5         print('触发set')
 6     def __delete__(self, instance):
 7         print('触发delete')
 8 
 9 #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
10 f1=Foo()
11 f1.name='egon'
12 f1.name
13 del f1.name

描述符触发情况:

实例中的部分属性被代理了

 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 #描述符Int
11 class Int:
12     def __get__(self, instance, owner):
13         print('Int调用')
14     def __set__(self, instance, value):
15         print('Int设置...')
16     def __delete__(self, instance):
17         print('Int删除...')
18 
19 class People:
20     name=Str()
21     age=Int()
22     def __init__(self,name,age): #name被Str类代理,age被Int类代理,
23         self.name=name
24         self.age=age
25 
26 
27 p1=People('alex',18)
28 
29 #描述符Str的使用
30 p1.name
31 p1.name='egon'
32 del p1.name
33 
34 #描述符Int的使用
35 p1.age
36 p1.age=18
37 del p1.age
38 
39 #我们来瞅瞅到底发生了什么
40 print(p1.__dict__)
41 print(People.__dict__)
42 
43 #补充
44 print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
45 print(type(p1).__dict__ == People.__dict__)

分类

一 数据描述符:至少实现了__get__()和__set__()

class Foo:
     def __set__(self, instance, value):
         print('set')
     def __get__(self, instance, owner):
          print('get')

二 非数据描述符:没有实现__set__()

 class Foo:
     def __get__(self, instance, owner):
         print('get')

注意事项

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

class People:
    name=Str()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age


p1=People('egon',18)

#如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
p1.name='egonnnnnn'
p1.name
print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
del p1.name
数据描述符>实例属性
 1 class Foo:
 2     def func(self):
 3         print('我胡汉三又回来了')
 4 f1=Foo()
 5 f1.func() #调用类的方法,也可以说是调用非数据描述符
 6 #函数是一个非数据描述符对象(一切皆对象么)
 7 print(dir(Foo.func))
 8 print(hasattr(Foo.func,'__set__'))
 9 print(hasattr(Foo.func,'__get__'))
10 print(hasattr(Foo.func,'__delete__'))
11 #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了
12 #笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
13 #函数就是一个由非描述符类实例化得到的对象
14 #没错,字符串也一样
15 
16 
17 f1.func='这是实例属性啊'
18 print(f1.func)
19 
20 del f1.func #删掉了非数据
21 f1.func()
实例属性>非数据描述符
class Foo:
    def __set__(self, instance, value):
        print('set')
    def __get__(self, instance, owner):
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length


#name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
#对实例的属性操作,触发的都是描述符的
r1=Room('厕所',1,1)
r1.name
r1.name='厨房'



class Foo:
    def __get__(self, instance, owner):
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length


#name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
#对实例的属性操作,触发的都是实例自己的
r1=Room('厕所',1,1)
r1.name
r1.name='厨房'
再次验证:实例属性>非数据描述符
class Foo:
    def func(self):
        print('我胡汉三又回来了')

    def __getattr__(self, item):
        print('找不到了当然是来找我啦',item)
f1=Foo()

f1.xxxxxxxxxxx
非数据描述符>找不到

描述符与装饰器

class Typed():
    """这是一个描述符,用于限制 Dog()中实例的变量类型"""
    def __init__(self, key, type):
        self.key = key
        self.type = type
    def __get__(self, instance, owner):
        print("触发__get__")
        # print('get/instance:', instance)
        # print('get/owner',owner)
        return instance.__dict__[self.key]

    def __set__(self, instance, value): #instance就是d1,value就是被代理变量传入的值
        print("触发__set__")
        if type(value) is not self.type:
            raise TypeError('类型错误,请输入数字')
            return
        # print('set/instance:', instance)
        # print('set/value',value)
        instance.__dict__[self.key] = value

    def __delete__(self, instance):
        print('触发__delete__')
        instance.__dict__.pop(self.key)



class Dog():
    age = Typed('age', int)
    name = Typed('name', str)
    # ...
    def __init__(self, name, age):
        self.name = name
        self.age = age

d1 = Dog('小黄', 12)
# print(d1.__dict__)
# print(d1.age)
使用描述符限制参数的类型
# 利用装饰器给类的属性字典增加固定的键值对(写死了)
def foo(obj):
    print('====>')
    obj.x = 1
    obj.y = 2
    obj.z = 3
    return obj

@foo # Dog = foo(Dog)
class Dog():
    pass

d1 = Dog()
print(Dog.__dict__)
print(d1.__dict__)
类的装饰器01
class Mytype(type): #元类,用于产生类的类。 python中type就是元类
    def __init__(self, a, b, c):
        pass
    def __call__(self, *args, **kwargs):
        obj = object.__new__(self)
        self.__init__(obj, *args, **kwargs)
        return obj #返回实例


class Dog(metaclass=Mytype): #Dog = Mytype(Dog, 'Dog', (父类), {}) --> __init__
    def __init__(self, name):
        self.name = name

#对象名加括号会触发类中的call方法
#类也可以看作是对象,一切皆对象
d1 = Dog('小黄')
print(d1.__dict__)
# d1()
类的装饰器02
 1 class Typed():
 2     """这是一个描述符,用于限制 Dog()中实例的变量类型"""
 3     def __init__(self, key, type):
 4         self.key = key
 5         self.type = type
 6     def __get__(self, instance, owner):
 7         # print("触发__get__")
 8         # print('get/instance:', instance)
 9         # print('get/owner',owner)
10         return instance.__dict__[self.key]
11 
12     def __set__(self, instance, value): #instance就是d1,value就是被代理变量传入的值
13         # print("触发__set__")
14         if type(value) is not self.type:
15             raise TypeError('类型错误,请输入数字')
16             return
17         # print('set/instance:', instance)
18         # print('set/value',value)
19         instance.__dict__[self.key] = value
20 
21     def __delete__(self, instance):
22         # print('触发__delete__')
23         instance.__dict__.pop(self.key)
24 
25 
26 def deco(**kwargs):
27     """装饰器"""
28     def foo(obj):
29         for k, v in kwargs.items():
30             setattr(obj, k, Typed(k,v)) #age = Typed('age', int)
31         return obj
32     return foo
33 
34 @deco(name=str, age=int)
35 class Dog():
36     # age = Typed('age', int)
37     # name = Typed('name', str)
38     def __init__(self, name, age, gender):
39         self.name = name
40         self.age = age
41         self.gender = gender
42 
43 print(Dog.__dict__)
44 d1 = Dog('小黄', 23, '')
45 print(d1.__dict__)
利用装饰器和描述符限制参数的类型
 1 class deco():
 2     """装饰器 @"""
 3     def __init__(self, fun):
 4         self.fun = fun
 5 
 6     def __get__(self, instance, owner):
 7         """实现描述符"""
 8         # print('form get')
 9         # print(instance)
10         # print(owner)
11         if not instance:
12             return self
13         return self.fun(instance) # d1.info = d1.info.fun(d1)
14 
15 
16 
17 class Dog():
18     def __init__(self, name, age):
19         self.name = name
20         self.age = age
21 
22     # @property # info = property(info)
23     @deco #info = deco(info) 返回一个deco对象
24     def info(self):
25         x = '这条狗的信息------>name:{}, age:{}'.format(self.name, self.age)
26         return x
27 
28 d1= Dog('小黄', 23)
29 print(d1.info)
30 # print(Dog.info)
利用装饰器和描述符实现property

 

__enter__和__exit__

在操作文件对象时的with语句:

 with open('a.txt') as f:
   '代码块'

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

 1 class File():
 2     """自定义的File类,演示上下文协议的实现过程"""
 3     def __init__(self, filename):
 4         self.filename = filename
 5     def __enter__(self):
 6         print('出现with语句时会触发__enter__()函数,并将函数返回值赋予as声明的变量')
 7         return self
 8     def __exit__(self, exc_type, exc_val, exc_tb): #with代码块运行完或with代码块抛异常都会触发__exit__
 9         print('with中的代码块执行结束或抛异常会执行__exit__()')
10 
11 with File('a.txt') as f: # File('a.txt')会触发__enter__()函数,并将函数的返回值赋给 f
12     print('文件名是:{}'.format(f.filename))
13     print('在with代码块中')
14 
15 print('在with外')
16 
17 # 执行结果:
18 # 出现with语句时会触发__enter__()函数,并将函数返回值赋予as声明的变量
19 # 文件名是:a.txt
20 # 在with中
21 # with中的代码块执行结束或抛异常会执行__exit__()
22 # 在with外

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

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

上下文管理协议作用:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,无须再去关心这个问题,这将大有用处

metaclass

一切皆对象  ==> 类也是一种对象,那么创建类这种对象的类就是元类

class Mytype(type): #元类,用于产生类的类。 python中type就是元类
    def __init__(self, a, b, c):
        pass
    def __call__(self, *args, **kwargs):
        obj = object.__new__(self)
        self.__init__(obj, *args, **kwargs)
        return obj #返回实例


class Dog(metaclass=Mytype): #Dog = Mytype(Dog, 'Dog', (父类), {}) --> __init__
    def __init__(self, name):
        self.name = name

#对象名加括号会触发类中的call方法
#类也可以看作是对象,一切皆对象
d1 = Dog('小黄')
print(d1.__dict__)
# d1()

 

参考

https://www.cnblogs.com/linhaifeng/articles/6204014.html

posted @ 2019-10-05 13:32  西伯利亚狼dreamer  阅读(183)  评论(0编辑  收藏  举报