目录
一、元类(***)
一切源自于一句话:python中一切皆为对象
1.1、元类介绍
元类—》OldboyTeacher类----》obj
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)
obj = OldboyTeacher('egon', 18) # 调用OldboyTeacher类=》对象obj
# 调用元类=》OldboyTeacher类
print(type(obj)) #<class '__main__.OldboyTeacher'>
print(type(OldboyTeacher)) # <class 'type'>
结论:默认的元类是type,默认情况下我们用class关键字定义的类都是由type产生的
1.2、class关键字底层的做了哪些事
# 1、先拿到一个类名
class_name = "OldboyTeacher"
# 2、然后拿到类的父类
class_bases = (object,)
# 3、再运行类体代码,将产生的名字放到名称空间中(放在字典中)
class_dic = {}
class_body = """
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)
"""
exec(class_body,{},class_dic) #exec可以运行字符串
print(class_dic) #{'school': 'oldboy', '__init__': <function __init__ at 0x000002DAC6C3B5E0>, 'say': <function say at 0x000002DAC6C3B550>}
# 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类
OldboyTeacher = type(class_name,class_bases,class_dic)
补充:exec的用法
#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}
1.3、自定义元类
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
pass
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)
# 1、先拿到一个类名:"OldboyTeacher"
# 2、然后拿到类的父类:(object,)
# 3、再运行类体代码,将产生的名字放到名称空间中{...}
# 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类
OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
1.4、自定义元类来控制OldboyTeacher类的产生
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
# print(self) # 类<class '__main__.OldboyTeacher'>
# print(class_name)
# print(class_bases)
# print(class_dic)
if not re.match("[A-Z]", class_name):
raise BaseException("类名必须用驼峰体")
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
# print("文档注释:",class_dic.get('__doc__'))
doc=class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
# OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
class OldboyTeacher(object,metaclass=Mymeta):
"""
adsaf
"""
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)
1.5、自定义元类来控制OldboyTeacher类的调用
关于__ call __的补充:
class Foo:
def __call__(self, *args, **kwargs):
print('================>')
print(self)
print(args)
print(kwargs)
obj1 = Foo()
obj1(1,2,3,a=1,b=2) # 调用对象其实就是在调用对象类中定义的绑定方法__call__
#
# obj2 = int(10)
# obj2()
# obj3 = list([1, 2, 3])
# obj3()
int类和list类里没有__call__(),所以没办法调用对象
#如果把OldboyTeacher也当做一个对象,那么在OldboyTeacher这个对象的类中也必然存在一个__call__方法
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
# print(self) # 类<class '__main__.OldboyTeacher'>
# print(class_name)
# print(class_bases)
# print(class_dic)
if not re.match("[A-Z]", class_name):
raise BaseException("类名必须用驼峰体")
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
# print("文档注释:",class_dic.get('__doc__'))
doc = class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
# res = OldboyTeacher('egon',18)
def __call__(self, *args, **kwargs):
# 1、先创建一个老师的空对象,调用__new__产生一个空对象obj
tea_obj = object.__new__(self)
# 2、调用老师类内的__init__函数,然后将老师的空对象连同括号内的参数的参数一同传给__init__ (#调用__init__初始化空对象obj)
self.__init__(tea_obj, *args, **kwargs)
tea_obj.__dict__ = {"_%s__%s" %(self.__name__,k): v for k, v in tea_obj.__dict__.items()}
# 3、将初始化好的老师对象赋值给变量名res
return tea_obj
# OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
class OldboyTeacher(object, metaclass=Mymeta):
"""
adsaf
"""
school = 'oldboy'
# tea_obj,'egon',18
def __init__(self, name, age):
self.name = name # tea_obj.name='egon'
self.age = age # tea_obj.age=18
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
res = OldboyTeacher('egon', 18)
print(res.__dict__)
# print(res.name)
# print(res.age)
# print(res.say)
# 调用OldboyTeacher类做的事情:
# 1、先创建一个老师的空对象
# 2、调用老师类内的__init__方法,然后将老师的空对象连同括号内的参数的参数一同传给__init__
# 3、将初始化好的老师对象赋值给变量名res
二、单例模式
2.1、实现方式1:classmethod
import settings
class MySQL:
__instance = None
def __init__(self, ip, port):
self.ip = ip
self.port = port
@classmethod
def singleton(cls):
if cls.__instance:
return cls.__instance
cls.__instance = cls(settings.IP, settings.PORT)
return cls.__instance
# obj1=MySQL("1.1.1.1",3306)
# obj2=MySQL("1.1.1.2",3306)
# print(obj1)
# print(obj2)
obj3 = MySQL.singleton()
print(obj3)
obj4 = MySQL.singleton()
print(obj4)
2.2、实现方式2:元类
import settings
class Mymeta(type):
__instance = None
def __init__(self,class_name,class_bases,class_dic):
self.__instance=object.__new__(self) # Mysql类的对象
self.__init__(self.__instance,settings.IP,settings.PORT)
def __call__(self, *args, **kwargs):
if args or kwargs:
obj = object.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
else:
return self.__instance
# MySQL=Mymeta(...)
class MySQL(metaclass=Mymeta):
def __init__(self, ip, port):
self.ip = ip
self.port = port
# obj1 = MySQL("1.1.1.1", 3306)
# obj2 = MySQL("1.1.1.2", 3306)
# print(obj1)
# print(obj2)
obj3 = MySQL()
obj4 = MySQL()
print(obj3 is obj4)
2.3、实现方式3:装饰器
settings.py文件内容
IP = "192.168.11.10"
PORT = 3306
import settings
def outter(func): # func = MySQl类的内存地址
_instance = func(settings.IP,settings.PORT)
def wrapper(*args,**kwargs):
if args or kwargs:
res=func(*args,**kwargs)
return res
else:
return _instance
return wrapper
@outter # MySQL=outter(MySQl类的内存地址) # MySQL=》wrapper
class MySQL:
def __init__(self, ip, port):
self.ip = ip
self.port = port
# obj1 = MySQL("1.1.1.1", 3306)
# obj2 = MySQL("1.1.1.2", 3306)
# print(obj1)
# print(obj2)
obj3 = MySQL()
obj4 = MySQL()
print(obj3 is obj4)
三、属性查找(了解)
在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象StanfordTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
n=444
def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
obj=self.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
class Bar(object):
n=333
class Foo(Bar):
n=222
class StanfordTeacher(Foo,metaclass=Mymeta):
n=111
school='Stanford'
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s says welcome to the Stanford to learn Python' %self.name)
print(StanfordTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为StanfordTeacher->Foo->Bar->object->Mymeta->type
于是属性查找应该分成两层,一层是对象层(基于c3算法的MRO)的查找,另外一个层则是类层(即元类层)的查找
#查找顺序:
#1、先对象层:StanfordTeacher->Foo->Bar->object
#2、然后元类层:Mymeta->type
依据上述总结,我们来分析下元类Mymeta中 __ call _ 里的self._ new__的查找
class Mymeta(type):
n=444
def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
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 StanfordTeacher(Foo,metaclass=Mymeta):
n=111
school='Stanford'
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s says welcome to the Stanford to learn Python' %self.name)
# def __new__(cls, *args, **kwargs):
# print('StanfordTeacher.__new__')
StanfordTeacher('lili',18) #触发StanfordTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
总结,Mymeta下的 __ call __ 里的self.__ new__ 在StanfordTeacher、Foo、Bar里都没有找到__new__的情况下,会去找object里的__new__,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__
把StanfordTeache当做类来看只能走到object,而把StanfordTeache当做对象来看,object没找到属性,会到元类中找