Python基础(十一)-面向对象四

一、上下文管理协议

1.1、什么叫上下文管理协议?

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

即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

with语句小结

with obj as  f:
    '代码块'
	
1)with obj ==>触发obj.__enter__(),拿到返回值
2)as f ==> f=返回值
3)with obj as f ==> f=obj.__enter__()
4) 执行代码块
    一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
    二:有异常的情况下,从异常出现的位置直接触发__exit__
	a:如果__exit__的返回值为True,代表吞掉了异常
	b:如果__exit__的返回值不为True,代表吐出了异常
	c:__exit__的的运行完毕就代表了整个with语句的执行完毕

1.2、方法使用

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:  #触发__enter__方法
    print('=====>执行代码块')
    print(f,f.name)  #<__main__.Open object at 0x0000022813E55C88> a.txt

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

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)


with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('异常产生')
print('0'*100) #------------------------------->不会执行

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

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)    #<class 'AttributeError'>
        print(exc_val)     #产生异常
        print(exc_tb)      #<traceback object at 0x000001E54419AF08>
        return True


with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('产生异常')
print('0'*100) #------------------------------->会执行

模拟open:

class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.filepath=filepath
        self.mode=mode
        self.encoding=encoding

    def __enter__(self):
        print('enter')
        self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)  #拿到文件句柄
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('exit')
        self.f.close()
        return True
    def __getattr__(self, item):
        return getattr(self.f,item)

with Open('a.txt','w') as f:  #f==open(self.filepath,mode=self.mode,encoding=self.encoding)
    print(f)                  #<_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>
    f.write('aaaaaa')
    f.wasdf #抛出异常,交给__exit__处理

1.3、上下文管理用途

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

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

二、描述符

2.1、什么是描述符?

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

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

2.2、描述符是干什么的?

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

#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

#描述符Int
class Int:
    def __get__(self, instance, owner):
        print('Int调用')
    def __set__(self, instance, value):
        print('Int设置...')
    def __delete__(self, instance):
        print('Int删除...')

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


p1=People('alex',18)  #触发str类的__set__,触发int类的__set__
print("========")

#描述符Str的使用
print(p1.name)  #Str调用 None
p1.name='egon'
del p1.name

#描述符Int的使用
p1.age   #Int调用
p1.age=18  #Int设置...
del p1.age  #Int删除...

print(p1.__dict__)  #{}
print(People.__dict__)  #{'__init__': <function People.__init__ at 0x000001714E4CE620>, 'age': <__main__.Int object at 0x000001714E4D46D8>, '__module__': '__main__', 'name': <__main__.Str object at 0x000001714E4D46A0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'People' objects>}


#补充
print(type(p1) == People) #True,type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__) #True

2.3、描述符的种类

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

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

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

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

2.4、描述符注意事项

1)描述符本身应该定义成新式类,被代理的类也应该是新式类

2)必须把描述符定义成这个类的类属性,不能为定义到构造函数中

3)要严格遵循该优先级,优先级由高到底分别是

  1. 类属性
  2. 数据描述符
  3. 实例属性
  4. 非数据描述符
  5. 找不到的属性(触发__getattr__)

2.5、描述符优先级

1)类属性>数据描述符

#数据描述符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类代理
        self.name=name
        self.age=age

#在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
People.name  #Str调用 ==>调用类属性name,本质就是在调用描述符Str,触发了__get__()
People.name="AAA"  #没有触发__set__
print(People.name)  #AAA
del People.name   #没有触发__delete__

'''
原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
People.name #调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()

People.name='egon' #直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
del People.name #同上
'''

2)数据描述符>实例属性

#数据描述符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类代理
        self.name=name
        self.age=age

p1=People('egon',18)  #触发__set__

#如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
p1.name="AAA"  #触发__set__
p1.name        #Str调用
print(p1.__dict__)  #{'age': 18} ==>并没有name属性 ==>name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
print(People.__dict__)  #'name': <__main__.Str object at 0x00000202C1DF5D30>
del p1.name  #Str删除...

3)实例属性>非数据描述符

#非数据描述符
class Str:
    def __get__(self, instance, owner):
        print('Str调用')

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

p1=People('egon',18) 
print(p1.name)  #egon   #没有触发__get__方法
print(p1.__dict__)  #{'age': 18, 'name': 'egon'}

4)非数据描述符>找不到

class Foo:
    def func(self):
        print('我胡汉三又回来了')

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

f1.xxxxxxxxxxx  #找不到了当然是来找我啦 xxxxxxxxxxx

2.6、描述符的使用

2.6.1、参数类型限制

python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3231.3)  #set---> <__main__.People object at 0x00000274AA9B4668> egon

#调用
print(p1.__dict__)  #{'salary': 3231.3, 'age': 18, 'name': 'egon'}
p1.name   #get---> <__main__.People object at 0x000001BCB50B4668> <class '__main__.People'>
print(p1.name)  #egon

#赋值
print(p1.__dict__)  #{'age': 18, 'name': 'egon', 'salary': 3231.3}
p1.name='egonlin'   #触发set
print(p1.__dict__)  #{'age': 18, 'name': 'egonlin', 'salary': 3231.3}

#删除
print(p1.__dict__)  #{'salary': 3231.3, 'name': 'egonlin', 'age': 18}
del p1.name         #触发delete
print(p1.__dict__)  #{'salary': 3231.3, 'age': 18}

实例二:

class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

# 疑问:如果我用类名去操作属性呢  ==>没有初始化实例
People.name #报错,错误的根源在于类去操作属性时,会把None传给instance

#修订__get__方法
class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
print(People.name)

#输出
# get---> None <class '__main__.People'>
# <__main__.Str object at 0x0000020ABCE05D68>

实例三:

class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Typed('name',str)
    age=Typed('name',int)
    salary=Typed('name',float)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

# p1=People(123,18,3333.3)  #TypeError: Expected <class 'str'>
# p1=People('egon','18',3333.3)  #TypeError: Expected <class 'int'>
p1=People('egon',18,3333)  #TypeError: Expected <class 'float'>

2.7、类的装饰器

1)类的装饰器--无参

def decorate(cls):
    print("类的装饰器")
    return cls

@decorate   #People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People("AAA",18,333.33)

2)类的装饰器--有参

def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>',kwargs)
        return cls
    return decorate
@typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3333.3)

#类的装饰器开始运行啦------> {'salary': <class 'float'>, 'age': <class 'int'>, 'name': <class 'str'>}

3)与描述符结合

class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)

def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>',kwargs)
        for name,expected_type in kwargs.items():
            setattr(cls,name,Typed(name,expected_type))
        return cls
    return decorate

@typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

print(People.__dict__)
#{'age': <__main__.Typed object at 0x000001C81FC155F8>, '__doc__': None, 'name': <__main__.Typed object at 0x000001C81FC156A0>, '__weakref__': <attribute '__weakref__' of 'People' objects>, 'salary': <__main__.Typed object at 0x000001C81FC15668>, '__dict__': <attribute '__dict__' of 'People' objects>, '__init__': <function People.__init__ at 0x000001C81FC0E620>, '__module__': '__main__'}
p1=People('egon',18,3333.3)
# p1=People('egon','18',3333.3)  #TypeError: Expected <class 'int'>

2.8、描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

2.9、自定制@property

利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

1)@property回顾

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @property
    def area(self):
        return self.width * self.length

r1=Room('AA',1,1)
print(r1.area)   #1

2)自定制property

class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:  #如果是类调用,会返回none  ==>Room.area
            return self
        return self.func(instance)

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @Lazyproperty     #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length

print(Room.__dict__)   #==>相当于在类中添加了一个属性 ==>'area': <__main__.Lazyproperty object at 0x000001ED1F016C50>}
r1=Room('alex',1,1)
print(r1.__dict__)  #{'length': 1, 'width': 1, 'name': 'alex'}
print(r1.area)   #先在实例属性中查找,没找到到类中查找,属性被描述符代理,触发__get__方法
print(Room.area)  #<__main__.Lazyproperty object at 0x000001D492CF5358>

3)实现延迟计算功能

class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            print('--->')
            # print(self)  #<__main__.Lazyproperty object at 0x0000024F8E435550>
            # print(self.func)  #<function Room.area at 0x000001E9D740E488>
            value=self.func(instance)  #self.func ===>传进来的area(),需要 将实例传进去 ==>值为1

            # print(self.func.__name__)  #area
            setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
            return value

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
    def area(self):
        return self.width * self.length

r1=Room('alex',1,1)
# print(r1.__dict__)  #{'width': 1, 'name': 'alex', 'length': 1}
print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后触发了area的__get__方法
# print(r1.__dict__)  #{'name': 'alex', 'area': 1, 'width': 1, 'length': 1}
# print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算,直接在实例属性字典中查找

4)小小改动

class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            value=self.func(instance)
            instance.__dict__[self.func.__name__]=value
            return value
        # return self.func(instance)
    def __set__(self, instance, value):   #实现了set方法,此时是数据描述符,优先级高于实例属性
        print('hahahahahah')

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length

print(Room.__dict__)  #==>'area': <__main__.Lazyproperty object at 0x000001D868D35550>}
r1=Room('alex',1,1)
print(r1.area)
print(r1.area)
print(r1.area)
print(r1.area) #缓存功能失效,每次都去找描述符了,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了

#输出
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1

2.10、自制@classmethod

class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
        def feedback(*args,**kwargs):
            print('在这里可以加功能啊...')
            return self.func(owner,*args,**kwargs)
        return feedback

class People:
    name='AAA'
    @ClassMethod           # say_hi=ClassMethod(say_hi)
    def say_hi(cls,msg):
        print('%s %s' %(cls.name,msg))

print(People.__dict__)  #==>'say_hi': <__main__.ClassMethod object at 0x000001EF2EA16B00>
People.say_hi('hello')

p1=People()
p1.say_hi('hello')

2.11、自制@staticmethod

class StaticMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
        def feedback(*args,**kwargs):
            print('在这里可以加功能啊...')
            return self.func(*args,**kwargs)
        return feedback

class People:
    @StaticMethod         #say_hi=StaticMethod(say_hi)
    def say_hi(x,y,z):
        print('------>',x,y,z)

print(People.__dict__)  #==>'say_hi': <__main__.StaticMethod object at 0x000001C3D63C5518>
People.say_hi(1,2,3)

p1=People()
p1.say_hi(4,5,6)

三、property使用

3.1、property本质

一个静态属性property本质就是实现了get,set,delete三种方法

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA   #get的时候运行我啊
f1.AAA='aaa'  #set的时候运行我啊
del f1.AAA    #delete的时候运行我啊

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#第二种写法
class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

3.2、property使用

使用示例一:

class Goods:

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price


obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
print(obj.price)
del obj.price     # 删除商品原价

四、元类

4.1、元类的引出

参考文档:https://www.cnblogs.com/linhaifeng/articles/8029564.html

python中一切都是对象,类本身也是一个对象,当使用class时,解释器会在加载class时就会创建一个对象(这里的对象值的是类而不是实例)

class Foo:
    pass

f1=Foo()

#使用type()查看类型
print(type(f1))  #<class '__main__.Foo'>
print(type(Foo)) #<class 'type'>
print(type(object))  #<class 'type'>

4.2、什么是元类

元类是类的类,是类的模板

元类是控制如何创建类的,正如类是创建对象的模板一样

元类的实例是类,正如类的实例是对象(f1对象是Foo类的一个实例,Foo类是type类的一个 实例)

type是python中一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类的实例

一个类如果没有声明自己的元类,默认他的类就是type,除了使用元类,用户也可以通过继承type来自定义元类

4.3、类的创建方式

方式一:

class Foo:
    def func(self):
        print("func")
print(Foo)   #<class '__main__.Foo'>
print(Foo.__dict__)
#{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, 'func': <function Foo.func at 0x0000024EDB86E158>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}

方式二:

def func(self):
    print("func")
x=1
Foo=type("Foo",(object,),{"func":func,"x":1})
print(Foo)  #<class '__main__.Foo'>
print(Foo.__dict__)
#{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, 'x': 1, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'func': <function func at 0x0000019343BA7F28>}

4.4、自定制元类

分析版:

class MyType(type):
    def __init__(self,a,b,c):
        print('元类的构造函数执行')
        # print(a)  #Foo
        # print(b)  #()
        # print(c)  #{'__module__': '__main__', '__init__': <function Foo.__init__ at 0x000001BE2AB9E400>, '__qualname__': 'Foo'}
    def __call__(self, *args, **kwargs):
        print('=-======>')
        print(self)   #<class '__main__.Foo'>
        print(args,kwargs)  #('alex',) {}
        obj=object.__new__(self) #object.__new__(Foo)-->f1  #创建Foo实例化出来的实例f1
        # print(obj)  #<__main__.Foo object at 0x0000020492F45550>
        self.__init__(obj,*args,**kwargs)  #Foo.__init__(f1,*arg,**kwargs),传入参数
        return obj  #返回实例化的对象给f1
class Foo(metaclass=MyType): #Foo=MyType(Foo,'Foo',(),{})---》__init__
    def __init__(self,name):
        self.name=name #f1.name=name
print(Foo)   #<class '__main__.Foo'>
f1=Foo('alex')  #实例() ==>调用父类的__call__方法
print(f1)       #<__main__.Foo object at 0x00000133525E5550>
print(f1.__dict__)  #{'name': 'alex'}

精简版:

#通过断点分析

class MyType(type):
    def __init__(self,a,b,c):
        print('元类的构造函数执行')
    def __call__(self, *args, **kwargs):
        obj=object.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj
class Foo(metaclass=MyType):
    def __init__(self,name):
        self.name=name
f1=Foo('alex')

posted @ 2019-09-10 18:26  运维人在路上  阅读(278)  评论(0编辑  收藏  举报