Python面向对象—面向对象高级


一、isinstance和issubclass

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

class Foo(object):
     pass
  
obj = Foo()
  
isinstance(obj, Foo)

issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo(object):
    pass

class Bar(Foo):
    pass

issubclass(Bar, Foo)

二、反射

1 什么是反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

2 python面向对象中的反射

通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

  • hasattr(obj, name):判断object中有没有一个name字符串对应的方法或属性
  • getattr(obj, name, default=None):从对象获取命名属性;getattr(x,‘y’)等价于x.y。当一个默认参数被给定时,当属性没有被指定时,它被返回存在;没有它,在这种情况下会引发异常。
  • setattr(x, y, v):将给定对象上的命名属性设置为指定值。setattr(x,‘y’,v)等价于“x.y=v”
  • delattr(x, y):从给定对象中删除命名属性。delattr(x,‘y’)等价于“del x.y”

四个方法的使用演示:

class BlackMedium:
    feature='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_house(self):
        print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name)
    def rent_house(self):
        print('%s 黑中介租房子啦,傻逼才租呢' %self.name)

b1=BlackMedium('万成置地','回龙观天露园')

#检测是否含有某属性
print(hasattr(b1,'name'))
print(hasattr(b1,'sell_house'))

#获取属性
n=getattr(b1,'name')
print(n)
func=getattr(b1,'rent_house')
func()

# getattr(b1,'aaaaaaaa') #报错
print(getattr(b1,'aaaaaaaa','不存在啊'))

#设置属性
setattr(b1,'sb',True)
setattr(b1,'show_name',lambda self:self.name+'sb')
print(b1.__dict__)
print(b1.show_name(b1))

#删除属性
delattr(b1,'addr')
delattr(b1,'show_name')
delattr(b1,'show_name111')#不存在,则报错

print(b1.__dict__)

'''
AttributeError: show_name111
True
True
万成置地
万成置地 黑中介租房子啦,傻逼才租呢
不存在啊
{'name': '万成置地', 'addr': '回龙观天露园', 'sb': True, 'show_name': <function <lambda> at 0x000001CA326CC430>}
万成置地sb
'''

类也是对象:

class Foo(object):
    staticField = "old boy"

    def __init__(self):
        self.name = 'wupeiqi'

    def func(self):
        return 'func'

    @staticmethod
    def bar():
        return 'bar'


print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))

'''
old boy
<function Foo.func at 0x000001A6B27AC5E0>
<function Foo.bar at 0x000001A6B27ACB80>
'''

反射当前模块成员:

import sys


def s1():
    print('s1')


def s2():
    print('s2')


this_module = sys.modules[__name__]

print(hasattr(this_module, 's1'))
print(getattr(this_module, 's2'))

'''
True
<function s2 at 0x000001F90DF9C670>
'''

导入其他模块,利用反射查找该模块是否存在某个方法

def test():
    print('from the test')
"""
程序目录:
    module_test.py
    index.py

当前文件:
    index.py
"""

import module_test as obj

#obj.test()

print(hasattr(obj,'test'))

getattr(obj,'test')()

获取当前模块:

current_module=__import__(__name__)
x=111
print(hasattr(current_module,"x"))
print(getattr(current_module,"x")) 

3 反射的好处

好处一:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

例如还未实现全部功能的代码:

class FtpClient:
    'ftp客户端,但是还么有实现具体的功能'
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr=addr

但不影响其他人的代码编写:

#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get=getattr(f1,'get')
    func_get()
else:
    print('---->不存在此方法')
    print('处理其他的逻辑')

好处二:动态导入模块(基于反射当前模块成员)
在这里插入图片描述


三 、__ setattr__,__ delattr__,__ getattr__

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    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')
        # del self.item #无限递归了
        self.__dict__.pop(item)

#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)

#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)

#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx

'''
----> from setattr
{}
----> from setattr
{}
----> from delattr
{}
----> from getattr:你找的属性不存在
'''

四、__ getattribute__

回顾__getattr__:

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__

'''
10
执行的是我
'''

__ getattribute__的用法:

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')

f1=Foo(10)
f1.x
f1.xxxxxx

'''
不管是否存在,我都会执行
不管是否存在,我都会执行
'''

当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
        raise AttributeError('哈哈')

f1=Foo(10)
f1.x
f1.xxxxxx

'''
不管是否存在,我都会执行
执行的是我
不管是否存在,我都会执行
执行的是我
'''

五、描述符(__ get__,__ set__,__ delete__)

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 描述符的作用

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

何时何地会触发描述符的三个方法的执行?

#描述符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的使用
p1.name
p1.name='egon'
del p1.name

#描述符Int的使用
p1.age
p1.age=18
del p1.age

#我们来瞅瞅到底发生了什么
print(p1.__dict__)
print(People.__dict__)

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

'''
Str设置...
Int设置...
Str调用
Str设置...
Str删除...
Int调用
Int设置...
Int删除...
{}
{'__module__': '__main__', 'name': <__main__.Str object at 0x000001FF6D153FD0>, 'age': <__main__.Int object at 0x000001FF6D153FA0>, '__init__': <function People.__init__ at 0x000001FF6D151700>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
True
True
'''

3 描述符种类

分为两种:
一 数据描述符:至少实现了__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')

4 注意事项

  • 一、描述符本身应该定义成新式类,被代理的类也应该是新式类
  • 二、必须把描述符定义成这个类的类属性,不能为定义到构造函数中
  • 三、要严格遵循该优先级,优先级由高到底分别是:类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()

5 描述符的使用

如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现,low

类的装饰器(无参):

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('egon',18,3333.3)

'''
类的装饰器开始运行啦------>
'''

类的装饰器(有参):

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)

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

描述符的使用:

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__)
p1=People('egon',18,3333.3)

'''
类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
{'__module__': '__main__', '__init__': <function People.__init__ at 0x00000243FCFB1790>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, 'name': <__main__.Typed object at 0x00000243FCFB53D0>, 'age': <__main__.Typed object at 0x00000243FCFB5310>, 'salary': <__main__.Typed object at 0x00000243FCFB52B0>}
set---> <__main__.People object at 0x00000243FCFB5FD0> egon
set---> <__main__.People object at 0x00000243FCFB5FD0> 18
set---> <__main__.People object at 0x00000243FCFB5FD0> 3333.3
'''

6 描述符总结

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

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


六、再看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
f1.AAA='aaa'
del f1.AAA

'''
get的时候运行我啊
set的时候运行我啊
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

'''
get的时候运行我啊
set的时候运行我啊
delete的时候运行我啊
'''

实例:

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     # 删除商品原价

七、__ setitem__,__ getitem__,__ delitem__

用于索引操作,如字典等,分别实现设置、获取、删除等功能

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

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18 # 自动触发执行__setitem__方法
f1['age1']=19
del f1.age1 # 自动触发执行__delattr__方法
del f1['age'] # 自动触发执行__delitem__方法
f1['name']='alex'
print(f1.__dict__)

'''
del obj.key时,我执行
del obj[key]时,我执行
{'name': 'alex'}
'''

八、__ str__,__ repr__,__ format__

改变对象的字符串显示__str__,__repr __,如果一个类中定义了__str __方法,那么在打印对象时默认输出该方法的返回值

自定制格式化字符串__format__

#_*_coding:utf-8_*_

format_dict={
    'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
    'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
    'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'School(%s,%s)' %(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec='nat'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)

'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))

'''
from repr:  School(oldboy1,北京)
from str:  (oldboy1,北京)
(oldboy1,北京)
oldboy1-北京-私立
私立:oldboy1:北京
私立/北京/oldboy1
oldboy1-北京-私立
'''

%s和%r:

class B:

     def __str__(self):
         return 'str : class B'

     def __repr__(self):
         return 'repr : class B'


b=B()
print('%s'%b)
print('%r'%b)

'''
str : class B
repr : class B
'''

九、__next__和__iter__实现迭代器协议

如果类中有__iter__方法,则该类是一个可迭代对象

其中 对象.__iter__的返回值是迭代器

示例:

class Foo:
    def __init__(self,x):
        self.x=x

    def __iter__(self):
        return self

    def __next__(self):
        n=self.x
        self.x+=1
        return self.x

f=Foo(3)
for i in f:
    print(i)

十、__ doc__

表示类的描述信息,且该属性无法被继承

class Foo:
    '我是描述信息'
    pass

print(Foo.__doc__)

'''
我是描述信息
'''

十一、__ module__和__class__

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

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

示例文件lib/aa.py:

class C:

    def __init__(self):
        self.name = ‘SB'

index.py:

from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类

十二、__ del__

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

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。因此什么时候执行并不知道

示例:

class Foo:

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

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

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

十三、__ call__

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

注:

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

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

        print('__call__')


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

'''
__call__
'''

十四、__ enter__和__ exit__

我们知道在操作文件对象的时候可以这么写:

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

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

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:
    print('=====>执行代码块')
    # print(f,f.name)

'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
'''

__ 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) #------------------------------->不会执行

'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
<class 'AttributeError'>
***着火啦,救火啊***
<traceback object at 0x00000261F6ABDB80>
Traceback (most recent call last):
  File "D:\pystudy\fullstack\作业练习\脚本.py", line 18, in <module>
    raise AttributeError('***着火啦,救火啊***')
AttributeError: ***着火啦,救火啊***
'''

如果__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)
        print(exc_val)
        print(exc_tb)
        return True



with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
<class 'AttributeError'>
***着火啦,救火啊***
<traceback object at 0x00000240B7E8DC00>
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
'''

__ enter__和__ exit__的用途:

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

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


十五、__ len__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __len__(self):
        return len(self.__dict__)
a = A()
print(len(a))

'''
2
'''

十六、__ hash__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __hash__(self):
        return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))

'''
-9051584000431974590
'''

十七、__ eq__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __eq__(self,obj):
        if  self.a == obj.a and self.b == obj.b:
            return True
a = A()
b = A()
print(a == b)

'''
True
'''

一道面试题:

class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):
        return hash(self.name+self.sex)

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:return True


p_lst = []
for i in range(84):
    p_lst.append(Person('egon',i,'male'))

print(p_lst)
print(set(p_lst))
'''
[<__main__.Person object at 0x0000016F4A143FD0>, <__main__.Person object at 0x0000016F4A143F70>, <__main__.Person object at 0x0000016F4A143E80>, <__main__.Person object at 0x0000016F4A143DC0>, <__main__.Person object at 0x0000016F4A143D60>, <__main__.Person object at 0x0000016F4A143790>, <__main__.Person object at 0x0000016F4A143730>, <__main__.Person object at 0x0000016F4A1436D0>, <__main__.Person object at 0x0000016F4A143670>, <__main__.Person object at 0x0000016F4A143610>, <__main__.Person object at 0x0000016F4A1435B0>, <__main__.Person object at 0x0000016F4A143550>, <__main__.Person object at 0x0000016F4A1434F0>, <__main__.Person object at 0x0000016F4A143490>, <__main__.Person object at 0x0000016F4A143430>, <__main__.Person object at 0x0000016F4A1433D0>, <__main__.Person object at 0x0000016F4A143370>, <__main__.Person object at 0x0000016F4A143310>, <__main__.Person object at 0x0000016F4A1432B0>, <__main__.Person object at 0x0000016F4A143250>, <__main__.Person object at 0x0000016F4A1431F0>, <__main__.Person object at 0x0000016F4A143190>, <__main__.Person object at 0x0000016F4A143130>, <__main__.Person object at 0x0000016F4A1430D0>, <__main__.Person object at 0x0000016F4A143070>, <__main__.Person object at 0x0000016F4A142FD0>, <__main__.Person object at 0x0000016F4A142F70>, <__main__.Person object at 0x0000016F4A142F10>, <__main__.Person object at 0x0000016F4A142EB0>, <__main__.Person object at 0x0000016F4A142E50>, <__main__.Person object at 0x0000016F4A142DF0>, <__main__.Person object at 0x0000016F4A142D90>, <__main__.Person object at 0x0000016F4A142D30>, <__main__.Person object at 0x0000016F4A142CD0>, <__main__.Person object at 0x0000016F4A142C70>, <__main__.Person object at 0x0000016F4A142C10>, <__main__.Person object at 0x0000016F4A142BB0>, <__main__.Person object at 0x0000016F4A142880>, <__main__.Person object at 0x0000016F4A142820>, <__main__.Person object at 0x0000016F4A1427C0>, <__main__.Person object at 0x0000016F4A142760>, <__main__.Person object at 0x0000016F4A142700>, <__main__.Person object at 0x0000016F4A1426A0>, <__main__.Person object at 0x0000016F4A142640>, <__main__.Person object at 0x0000016F4A1425E0>, <__main__.Person object at 0x0000016F4A142580>, <__main__.Person object at 0x0000016F4A142520>, <__main__.Person object at 0x0000016F4A1424C0>, <__main__.Person object at 0x0000016F4A142460>, <__main__.Person object at 0x0000016F4A142400>, <__main__.Person object at 0x0000016F4A1423A0>, <__main__.Person object at 0x0000016F4A142340>, <__main__.Person object at 0x0000016F4A1422E0>, <__main__.Person object at 0x0000016F4A142130>, <__main__.Person object at 0x0000016F4A1420D0>, <__main__.Person object at 0x0000016F4A142070>, <__main__.Person object at 0x0000016F4A127AC0>, <__main__.Person object at 0x0000016F4A127B50>, <__main__.Person object at 0x0000016F4A127BB0>, <__main__.Person object at 0x0000016F4A127C10>, <__main__.Person object at 0x0000016F4A127D00>, <__main__.Person object at 0x0000016F49F2B0A0>, <__main__.Person object at 0x0000016F49F2B070>, <__main__.Person object at 0x0000016F49F2BC10>, <__main__.Person object at 0x0000016F4A0C0A90>, <__main__.Person object at 0x0000016F49F25130>, <__main__.Person object at 0x0000016F49EFBBB0>, <__main__.Person object at 0x0000016F49EFBB50>, <__main__.Person object at 0x0000016F49A8B0D0>, <__main__.Person object at 0x0000016F4A145580>, <__main__.Person object at 0x0000016F4A145520>, <__main__.Person object at 0x0000016F4A1454C0>, <__main__.Person object at 0x0000016F4A145460>, <__main__.Person object at 0x0000016F4A145400>, <__main__.Person object at 0x0000016F4A1453A0>, <__main__.Person object at 0x0000016F4A145340>, <__main__.Person object at 0x0000016F4A1452E0>, <__main__.Person object at 0x0000016F4A145280>, <__main__.Person object at 0x0000016F4A145220>, <__main__.Person object at 0x0000016F4A1451C0>, <__main__.Person object at 0x0000016F4A145160>, <__main__.Person object at 0x0000016F4A145100>, <__main__.Person object at 0x0000016F4A1450A0>, <__main__.Person object at 0x0000016F4A145040>]
{<__main__.Person object at 0x0000016F4A143FD0>}

'''

十八、__ new__和元类metaclass

1 什么是元类

python中一切皆为对象。让我们先定义一个类:

class OldboyTeacher(object):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类OldboyTeacher得到的

t1=OldboyTeacher('egon',18)
print(type(t1)) #查看对象t1的类是<class '__main__.OldboyTeacher'>

如果一切皆为对象,那么类OldboyTeacher本质也是一个对象,既然所有的对象都是调用类得到的,那么OldboyTeacher必然也是调用了一个类得到的,这个类称为元类

于是我们可以推导出===>产生OldboyTeacher的过程一定发生了:OldboyTeacher=元类(…)

print(type(OldboyTeacher)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的OldboyTeacher,即默认的元类为type

在这里插入图片描述

2 class关键字创建类的流程分析

上文我们基于python中一切皆为对象的概念分析出:我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type

class关键字在帮我们创建类时,必然帮我们调用了元类OldboyTeacher=type(…),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分,分别是:

  1. 类名class_name=‘OldboyTeacher’
  2. 基类们class_bases=(object,)
  3. 类的名称空间class_dic,类的名称空间是执行类体代码而得到的

调用type时会依次传入以上三个参数

综上,class关键字帮我们创建一个类应该细分为以下四个过程
在这里插入图片描述
补充:exec的用法:

三个参数:

  • 参数一:包含一系列python代码的字符串
  • 参数二:全局作用域(字典形式),如果不指定,默认为globals()
  • 参数三:局部作用域(字典形式),如果不指定,默认为locals()

可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中

g={
    'x':1,
    'y':2
}
l={}

exec('''
global x,z
x=100
z=200

m=300
''',g,l)

print(g) #{'x': 100, 'y': 2,'z':200,......}
print(l) #{'m': 300}

3 自定义元类控制类的创建

一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类:

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    pass

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即OldboyTeacher=Mymeta(‘OldboyTeacher’,(object),{…}),调用Mymeta会先产生一个空对象OldoyTeacher,然后连同调用Mymeta括号内的参数一同传给Mymeta下的__init__方法,完成初始化,于是我们可以:

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self,class_name,class_bases,class_dic):
        # print(self) #<class '__main__.OldboyTeacher'>
        # print(class_bases) #(<class 'object'>,)
        # print(class_dic) #{'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x102b95ae8>, 'say': <function OldboyTeacher.say at 0x10621c6a8>}
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类的功能

        if class_name.islower():
            raise TypeError('类名%s请修改为驼峰体' %class_name)

        if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    """
    类OldboyTeacher的文档注释
    """
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

4 自定义元类控制类的调用

调用一个对象,就是触发对象所在类中的__call__方法的执行,如果把OldboyTeacher也当做一个对象,那么在OldboyTeacher这个对象的类中也必然存在一个__call__方法

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __call__(self, *args, **kwargs):
        print(self) #<class '__main__.OldboyTeacher'>
        print(args) #('egon', 18)
        print(kwargs) #{}
        return 123

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)



# 调用OldboyTeacher就是在调用OldboyTeacher类中的__call__方法
# 然后将OldboyTeacher传给self,溢出的位置参数传给*,溢出的关键字参数传给**
# 调用OldboyTeacher的返回值就是调用__call__的返回值
t1=OldboyTeacher('egon',18)
print(t1) 

'''
<class '__main__.OldboyTeacher'>
('egon', 18)
{}
123
'''

默认地,调用t1=OldboyTeacher(‘egon’,18)会做三件事:

  1. 产生一个空对象obj
  2. 调用__init__方法初始化对象obj
  3. 返回初始化好的obj

对应着,OldboyTeacher类中的__call__方法也应该做这三件事

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        #1、调用__new__产生一个空对象obj
        obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个OldboyTeacher的对象obj

        #2、调用__init__初始化空对象obj
        self.__init__(obj,*args,**kwargs)

        #3、返回初始化好的对象obj
        return obj

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(t1.__dict__) 

'''
{'name': 'egon', 'age': 18}
'''

上例的__call__相当于一个模板,我们可以在该基础上改写__call__的逻辑从而控制调用OldboyTeacher的过程,比如将OldboyTeacher的对象的所有属性都变成私有的

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        #1、调用__new__产生一个空对象obj
        obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个OldboyTeacher的对象obj

        #2、调用__init__初始化空对象obj
        self.__init__(obj,*args,**kwargs)

        # 在初始化之后,obj.__dict__里就有值了
        obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}
        #3、返回初始化好的对象obj
        return obj

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(t1.__dict__) 

'''
{'_OldboyTeacher__name': 'egon', '_OldboyTeacher__age': 18}
'''

上例中涉及到查找属性的问题,比如self.new,请看下一小节

5 属性查找

结合python继承的实现原理+元类重新看属性的查找应该是什么样子呢???

在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        obj=self.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj

class Bar(object):
    n=333

class Foo(Bar):
    n=222

class OldboyTeacher(Foo,metaclass=Mymeta):
    n=111

    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


print(OldboyTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type

于是属性查找应该分成两层,一层是对象层(基于c3算法的MRO)的查找,另外一个层则是类层(即元类层)的查找

在这里插入图片描述

查找顺序:
1、先对象层:OldoyTeacher->Foo->Bar->object
2、然后元类层:Mymeta->type

依据上述总结,我们来分析下元类Mymeta中__call__里的self.__new__的查找

class Mymeta(type): 
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        obj=self.__new__(self)
        print(self.__new__ is object.__new__) #True


class Bar(object):
    n=333

    # def __new__(cls, *args, **kwargs):
    #     print('Bar.__new__')

class Foo(Bar):
    n=222

    # def __new__(cls, *args, **kwargs):
    #     print('Foo.__new__')

class OldboyTeacher(Foo,metaclass=Mymeta):
    n=111

    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


    # def __new__(cls, *args, **kwargs):
    #     print('OldboyTeacher.__new__')


OldboyTeacher('egon',18) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找

总结,Mymeta下的__call__里的self.new__在OldboyTeacher、Foo、Bar里都没有找到__new__的情况下,会去找object里的__new,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__

我们在元类的__call__中也可以用object.new(self)去造对象

在这里插入图片描述
但我们还是推荐在__call__中使用self.new(self)去创造空对象,因为这种方式会检索三个类OldboyTeacher->Foo->Bar,而object.__new__则是直接跨过了他们三个

最后说明一点

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    n=444

    def __new__(cls, *args, **kwargs):
        obj=type.__new__(cls,*args,**kwargs) # 必须按照这种传值方式
        print(obj.__dict__)
        # return obj # 只有在返回值是type的对象时,才会触发下面的__init__
        return 123

    def __init__(self,class_name,class_bases,class_dic):
        print('run。。。')


class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    n=111

    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


print(type(Mymeta)) #<class 'type'>
# 产生类OldboyTeacher的过程就是在调用Mymeta,而Mymeta也是type类的一个对象,那么Mymeta之所以可以调用,一定是在元类type中有一个__call__方法
# 该方法中同样需要做至少三件事:
# class type:
#     def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
#         obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象
#         self.__init__(obj,*args,**kwargs) 
#         return obj

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值