一. 元类介绍
"""
什么是元类?
引入: python之中一切皆对象, 类也是对象.
即可调用类实例化产出对象, 那也可调用元类实例化产生类
"""
# 关系: 元类 --> 实例化 --> 类(People) --> 实例化 --> 对象(obj)
class People(object):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print(f'姓名:{self.name} 年龄:{self.age}')
# 1. 如何得到对象?
# obj = 类()
obj = People('egon', 19)
# 2. 如何得到类?
# People = 调用类()
# 3. 查看内置的元类:
"""
1、type是内置的元类
2、我们用class关键字定义的所有的类以及内置的类都是由元类type实例化产生
"""
print(type(obj)) # type对象查看到对象中的类。<class '__main__.People'>
print(type(People)) # type类查看到People的元类。<class 'type'>
print(type(int)) # 内置类int的元类也是type. <class 'type'>
二. class关键字创造类People的步骤
1. exec的用法介绍:
"""
参数一:包含一系列python代码的字符串
参数二:全局作用域(字典形式),如果不指定,默认为globals()
参数三:局部作用域(字典形式),如果不指定,默认为locals()
可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
"""
global_dic = {
'x': 1,
'y': 2
}
local_dic = {}
exec("""
global x,z # 这里在局部名称空间local_dic中拿到全局名称空间中的x,z进行修改.
x=100
z=200
m=300 # 这里在局部名称空间local_dic中定义了{"m": 300}这个名字
""", global_dic, local_dic)
# print(global_dic) # {'x': 100, 'y': 2,'z':200,......}
print(local_dic) # {'m': 300}
2. 实例: 由exec来创建类的名称空间.
# 类有三大特征:
# 2-1. 类名
class_name = 'People'
# 2-2. 类的基类
class_bases = (object,)
# 2-3. 执行类体代码拿到的类的名称空间
class_dict = {}
class_body = """
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print(f'姓名:{self.name} 年龄:{self.age}')
"""
exec(class_body, {}, class_dict)
# print(class_dict) # {'__init__': <function __init__ at 0x000001FB9A58C940>, 'say': <function say at 0x000001FB9A58CA60>}
# 2-4. 调用元类实例化产生类
People = type(class_name, class_bases, class_dict)
# 实例化创建的类People产生对象obj, 完成默认class机制关键类People的步骤
obj = People('egon', 18)
print(obj) # <__main__.People object at 0x000001FB9A4BA1F0>
obj.say() # 姓名:egon 年龄:18
三. 自定义元类来控制类的产生
class MyMeta(type): # 注意: 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
# cls=当前所在的类MyMeta # *args, **kwargs调用MyMeta时括号内的参数. 返回一个空对象
def __new__(cls, *args, **kwargs):
# return type.__new__(cls, *args, **kwargs)
# return super(MyMeta, cls).__new__(cls, *args, **kwargs)
return super().__new__(cls, *args, **kwargs)
# self=执行__new__创建的空对象
# class_name=People
# class_bases=()
# class_dict=执行People类体代码产生的名称空间. ==> People.__dict__
def __init__(self, class_name, class_bases, class_dict):
# type.__init__(self, class_name, class_bases, class_dict)
# super(MyMeta, self).__init__(class_name, class_bases, class_dict)
super().__init__(class_name, class_bases, class_dict)
# 自定义元类控制类People产生!!!: 必须要有文档注释+类名首字母必须大写+类中必须定义func可调用函数
if class_name.islower():
raise NameError("类名必须大写!")
# '__doc__' not in class_dict # 判断文档注释不存在
# class_dict['__doc__'].strip(' \n') # 判断文件注册存在且为空和换行的情况.
# 提示: 为什么这里使用'字典[]'取值不会出现'__doc__'没有报错的情况, 由成员运算or的短路运算发现运行到第一个条件只有为False的情况下才会执行后面的第二个条件, 那么第一个条件成立表明'__doc__'一定是存在的, 所以后面可以这么使用.
print(class_dict)
if '__doc__' not in class_dict or not class_dict['__doc__'].strip(' \n'):
raise TypeError("类中必须指定文档注释, 且不能为空!")
if 'func' not in class_dict or not callable(class_dict['func']):
raise NameError("类中必须定义func这个可调用函数!")
# People = MyMeta('People', (object, ), {执行类体代码产生People的名称空间})
# 调用Mymeta发生三件事: 调用Mymeta就是调用type.__call__
'''
1. 调用Mymeta类内的__new__方法造了一个空对象 ==> People.
2. 调用Mymeta类内的__init__方法, 完成刚刚造好的空对象初始化的操作.
3. 返回初始化好的对象
'''
class People(metaclass=MyMeta):
"""文档注释"""
# func = 0
def func(self):
pass
# 强调!!:
'''
只要是调用类, 那么就是一次调用
1. 类内的__new__
2. 类内的__init__
'''
四. __call__
使用介绍
# __call__触发条件: 在对象的类中定义, 实现在对象被调用时触发.
class People:
def __init__(self, name):
self.name = name
def __call__(self, *args, **kwargs):
print(self) # <__main__.People object at 0x000001A79667F490>
print(args) # (1, 2, 3)
print(kwargs) # {'a': 4}
return 111
obj = People('egon')
# 如果想然一个'对象'可以加括号调用, 需要在该'对象'的类中添加一个方法__call__
res = obj(1, 2, 3, a=4)
print(res) # 111
# 总结:
"""
对象() --> 类内定义了__call__
类() ---> 自定义元类内定义了__call__
自定义元类() --> 内置元类内定义了__call__
"""
五. 自定义元类控制类的调用(类的对象的产生)
class MyMeta(type):
# 调用People触发了MyMeta中定义的__call__的执行.
def __call__(self, *args, **kwargs):
# 1. MyMeta.__call__函数会先调用People内的__new__, 实例化产生一个空对象people_obj
print(self) # <class '__main__.People'>
print(args, kwargs) # ('egon', 18) {}
people_obj = self.__new__(self) # People.__new__
# people_obj = object.__new__(self) # 不推荐使用, 推荐使用上面的形式. 这种形式直接指明道姓获取object中的__new__, 即使People类中定义了__new__想用自己的都用不了.
print(self.__new__ is object.__new__) # False ==> People.__new__ is object.__new__
# 2. MyMeta.__call__函数接着调用People内的__init__完成空对象people_obj的初始化操作
# super(MyMeta, self).__init__(people_obj, *args, **kwargs)
self.__init__(people_obj, *args, **kwargs)
# 自定义元类控制类的对象的产生这一步可以对象的属性进行操作!!!!
# 将对象初始化完毕之前对对象中所有的属性进行隐藏操作 (注意: 考虑对象中可能默认就指定的隐藏属性)
people_obj.__dict__ = {attr if attr.startswith(f"_{self.__name__}__") else f'_{self.__name__}__{attr}': value for
attr, value in obj.__dict__.items()}
# 3. MyMeta.__call__函数会返回一个初始化好的对象people_obj
return people_obj
# 调用自定义元类MyMeta, 产生类People, 发生了三件事:
'''
People = MyMeta(class_name, class_bases, class_dict) ==> type.__call__干了三件事
1. type.__call__函数会先调用MyMeta中的__new__, 产生一个空类对象people_obj
2. type.__call__函数会调用MyMeta中的__init__, 为这个people_obj空类对象进行初始化.
3. type.__call__函数会返回一个返回值, 返回值会返回一个初始好的people_obj空类对象
'''
class People(metaclass=MyMeta):
def __init__(self, name, age):
self.name = name
self.__age = age
self._score = {}
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
# 调用类People, 产生obj对象, 发生了三件事:
'''
obj = People('egon', 18) ==> MyMeta.__call__干了三件事
1. MyMeta.__call__函数会先People内的__new__, 产生一个空对象obj
2. MyMeta.__call__函数接着调用People内的__init__, 为obj进行初始化
3. MyMeta.__call__函数会返回一个返回值, 返回值为初始化好的obj对象
'''
obj = People('egon', 18)
print(obj.__dict__)
六. 属性查找
# 属性查找的原则:对象-》类-》父类 切记!!!:父类 不是 元类
"""
属性查找:
1. 对象的属性查找: 只会找到object. 不会找元类.
2. 类的属性查找: 在找到object没有找到时, 会去元类中找.
使用__new__创建空对象的二种方式:
obj = object.__new__(self)
obj = self.__new__(self) # 推荐用这一种, 这种查找方式会按照继承的方式查找, 而object直接找到源底部的__new__方法, 如果需要使用自己的__new__或者其它父类的确无法使用, 有局限性.
"""
class Mymeta(type):
n = 444
def __call__(self, *args, **kwargs): # self=<class '__main__.Teacher'>
print(self.__new__)
obj = self.__new__(self) # Teacher.__new__
# obj=object.__new__(self)
# print(self.__new__ is object.__new__) # True
self.__init__(obj, *args, **kwargs)
return obj
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 Teacher(Foo, metaclass=Mymeta):
# n = 111
def __init__(self, name, age):
self.name = name
self.age = age
# def __new__(cls, *args, **kwargs):
# print('Foo.__new__')
obj = Teacher('lili', 18)
# 对象的属性查找
# print(obj.__dict__) # {'name': 'lili', 'age': 18}
# print(obj.n)
# 类的属性查找
print(Teacher.n)
七. 实现单例模式的六种方式(面试必要会3种)
0. 单例介绍
什么是单例模式?
即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间
什么时候使用?
如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了. 也就是说你调用类实例化对象,为对象进行了复制,
单例模式是一个软件的设计模式,为了保证一个类,
实现的思路:
单例的本质就是调用类去实例化出指向同一内存地址的对象,所以我们这里的关注点就是基于这个类.
1. 类方法classmethod实现
# settings.py文件内容如下
"""
HOST = '127.0.0.1'
PORT = 8080
"""
import settings
class MySql:
__instance = None # instance 实例
def __init__(self, ip, port):
self.ip = ip
self.port = port
@classmethod
def singleton(cls): # singleton 单例模式
if not cls.__instance:
cls.__instance = cls(settings.IP, settings.PORT)
return cls.__instance
obj1 = MySql('127.0.0.1', 8080)
obj2 = MySql('127.0.0.1', 8080)
print(obj1 is obj2) # False
# 使用单例模式
obj1 = MySql.singleton()
obj2 = MySql.singleton()
print(obj1 is obj2) # True
2. 通过调用类实例化对象时自动触发的__new__
来实现
class MySql:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = object.__new__(cls)
return cls._instance
obj1 = MySql()
obj2 = MySql()
print(obj1 is obj2) # True
3. 自定义元类实现
"""
思路
利用元类特性: 使用自定义元类实例化类的时候为类绑定一个默认的对象
"""
import settings
class MyMeta(type):
def __init__(self, class_name, class_bases, class_dict):
# self._instance = object.__new__(self)
# self.__init__(self._instance, settings.IP, settings.PORT)
# 上述两步可以合成下面一步
self._instance = super().__call__(settings.IP, settings.PORT)
super().__init__(class_name, class_bases, class_dict)
# 控制Ftp的调用形式: 如果调用Ftp传参了,那么将不是一个单例模式. 如果只是Ftp直接加括号调用, 那么就返回文件中拿到的单例.
def __call__(self, *args, **kwargs):
if args or kwargs:
obj = self.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
return self._instance
# MySql = MyMeta('MyMeta', (), {执行类体代码产生的名称空间})
class MySql(metaclass=MyMeta):
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySql('127.0.0.1', 8080)
obj2 = MySql('127.0.0.1', 8080)
print(obj1 is obj2) # False
obj1 = MySql()
obj2 = MySql()
print(obj1 is obj2) # True
4. 函数装饰器实现
'''
利用装饰器语法糖的特性: MySql = wrapper = singleton(MySql) 其中MySql就是被装饰的类.
'''
import settings
def singleton(cls):
_instance = cls(settings.IP, settings.PORT)
def wrapper(*args, **kwargs):
if args or kwargs:
return cls(*args, **kwargs)
return _instance
return wrapper
@singleton # MySql = wrapper = singleton(MySql)
class MySql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySql('127.0.0.1', 8080)
obj2 = MySql('127.0.0.1', 8080)
print(obj1 is obj2) # False
obj1 = MySql()
obj2 = MySql()
print(obj1 is obj2) # True
5. 类装饰器实现
'''
利用装饰器语法糖的特性: MySql = Singleton(MySql), 装饰MySql类就是对Singleton类的实例化. 调用MySql时就会触发实例化该对象的类Singleton中的__call__方法. (提示: self.cls就是MySql.cls --> 这里的cls就是原来的类名)
'''
import settings
class Singleton:
_instance = None
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwargs):
if args or kwargs:
return self.cls(*args, **kwargs)
if not self._instance:
self._instance = self.cls(settings.IP, settings.PORT)
return self._instance
@Singleton # MySql = Singleton(MySql)
class MySql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySql('127.0.0.1', 8080)
obj2 = MySql('127.0.0.1', 8080)
print(obj1 is obj2) # False
obj1 = MySql()
obj2 = MySql()
print(obj1 is obj2) # True
6. 导入模块实现
"""
利用模块只要导入一次, 多次导入都是以第一次为准.
"""
# 创建singleton.py文件放入以下内容:
'''
import settings
class MySql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
instance = MySql(settings.IP, settings.PORT)
'''
import singleton
obj1 = singleton.MySql('127.0.0.1', 8080)
obj2 = singleton.MySql('127.0.0.1', 8080)
print(obj1 is obj2) # False
obj1 = singleton.instance
obj2 = singleton.instance
print(obj1 is obj2) # True