一、 元类(***)难点
定义类,控制对象产生
定义元类,控制类产生(看源码时用)
一切源自于一句话:python中一切皆为对象
1、元类介绍
对象是调用类得到
元类是调用元类得到
查看类(就是类型):type(obj)===>OldboyTeacher
查看元类(就是类型):type(OldboyTeacher)==>type
# 元类=》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)) print(type(OldboyTeacher)) # 结论:默认的元类是type,默认情况下我们用class关键字定义的类都是由type产生的
2、class关键字底层的做了哪些事造出来类?
一定是调用了type得到返回值,把返回值赋值给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) # print(class_dic) # 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类 OldboyTeacher = type(class_name,class_bases,class_dic)
3、自定义元类
只有继承了type类的类才是自定义的元类
type是内置的元类,type内部一定有个__init__用法
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,),{...})
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)
5、自定义元类来控制OldboyTeacher类的调用
自定义元类的目的是:定义规范
这里涉及到如何隐藏属性问题:其实就是利用字典的k:v(此例子中有)
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、先创建一个老师的空对象 tea_obj = object.__new__(self) # 2、调用老师类内的__init__函数,然后将老师的空对象连同括号内的参数的参数一同传给__init__ 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
6、仅供理解的例子
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()
二、 单例模式
字面意思:保证一个类仅有一个实例,并只提供一个访问它的全局访问点
目的:节省内存空间
设计模式的一种
#优点: 1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间; 2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用; 3、单例可长驻内存,减少系统开销。 #缺点: 1、单例模式的扩展是比较困难的; 2、赋于了单例以太多的职责,某种程度上违反单一职责原则(六大原则后面会讲到); 3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试; 4、单例模式在某种情况下会导致“资源瓶颈”。 #应用举例: 1、生成全局惟一的序列号; 2、访问全局复用的惟一资源,如磁盘、总线等; 3、单个对象占用的资源过多,如数据库等; 4、系统全局统一管理,如Windows下的Task Manager; 5、网站计数器。
三种方式实现单例模式
IP='192.1.1.1' PORT=3306
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:元类
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)
3、实现方式3:装饰器
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)
三、属性查找
按照之前的查找顺序
在有元类的情况下的属性查找顺序:一直找到父类找到object后再找元类
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__') pass class Foo(Bar): # n=222 # def __new__(cls, *args, **kwargs): # print('Foo.__new__') pass class OldboyTeacher(Foo,metaclass=Mymeta): # n=111 school='oldboy' def __init__(self,name,age): # self.n=0 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__') # obj=OldboyTeacher('egon',18) # print(obj.n) print(OldboyTeacher.n)
---34---