目录
一、补充:面向对象的静态方法
功能体中永远不需要使用类与对象
既不绑定给类,也不绑定给对象
class A:
def t1(self):
pass
@classmethod
def t2(cls):
pass
@staticmethod
def t3():
pass
二、eval与exec内置函数
将字符串作为执行目标,得到相应结果
abc = 10
print(eval(abc)) # eval() arg 1 must be a string, bytes or code object
eval内置函数的使用场景:
1、执行字符串会得到相应的执行结果,有返回值
2、一般用于类型转化,得到dict,list,tuple等
dic_str = "{'a': 1, 'b': 2, 'c': 3}"
print(eval(dic_str),type(eval(dic_str))) # {'a': 1, 'b': 2, 'c': 3} <class 'dict'>
list_str = "[1, 2, 3, 4, 5]"
print(eval(list_str),type(eval(list_str))) # [1, 2, 3, 4, 5] <class 'list'>
tuple_str = "(1, 2, 3, 4, 5)"
print(eval(tuple_str),type(eval(tuple_str))) # (1, 2, 3, 4, 5) <class 'tuple'>
exec应用场景
1.执行字符串没有执行结果(没有返回值)
2.将执行的字符串中产生的名字形成对应的局部名称空间
source = '''
name = 'Bob'
age = 20
'''
class A:
pass
a = A()
dic = {}
exec(source, {}, dic)
print(dic) # {'name': 'Bob', 'age': 20}
a.__dict__ = dic
print(a.__dict__) # {'name': 'Bob', 'age': 20}
print(a.name) # Bob
print(a.age) # 20
三、元类
3.1、什么事元类?
在python中,一切皆对象。而对象都是调用类实例化后得到的。既然一切皆对象,那么类也是对象,用来创建类的类就是元类:type。即类是type类的对象。
class OldboyTeacher:
school = 'oldboy'
def __init__(self, name):
self.name = name
def score(self):
print('%s is scoring' % self.name)
tea1 = OldboyTeacher('shj')
print(type(tea1)) # <class '__main__.OldboyTeacher'>
print(type(OldboyTeacher)) # <class 'type'>
tea1是调用OldboyTeacher类得到的,OldboyTeacher类也是一个对象,只要是对象,都是调用一个类实例化得到的,即OldboyTeacher=type(*args,**kwargs)
关系:
调用元类(实例化)-->自定义的类
调用自定义的类-->自定义的对象
3.2、不依赖class创建一个自定义类
一、class关键字创建自定义类的底层原理
1、先拿到类名
2、再拿到类的基类们,默认都是object
3、然后拿到类的名称空间
4、调用元类(type)实例化得到自定义的类:OldboyTeacher = type('Oldboyteacher',(object,),{……})
二、自定义类的三个关键组成:
1、类名
2、类的基类们
3、类的名称空间
案例:
class_name = 'Oldboy'
class_bases = (object,)
class_dic = {}
class_body = """
school = 'oldboy'
def __init__(self, name):
self.name = name
def score(self):
print('%s is scoring' % self.name)
"""
exec(class_body,{},class_dic)
obj = type(class_name, class_bases, class_dic)
# 验证
print(obj) # <class '__main__.Oldboy'>,成功创建类Oldboy
print(obj.__dict__)
t = obj('shj') # 可以正常实例化,得到名称空间,用来存放产生的名字
print(t.__dict__) # {'name': 'shj'}
3.3、自定义元类中的一些方法
# 自定义异常
class CountError(Exception):
pass
class MyMeta(type): # 但凡继承了type的类才能成为元类,否则就只是一个普通的类
# 自定义元类,重写init方法的目的:
# 1.该方法是从type中继承来的,所以参数同type的init
# 2.最终的工作(如果开辟空间,如果操作内存)还是要借助type
# 3.在交给type最终完成工作之前,可以对类的创建加以限制 *****
def __init__(cls, class_name, bases, namespace):
print(cls) # # 继承MyMeta的类
print(class_name)
print(bases)
print(namespace)
# 需求:给元类控制的类的类名必须首字母大写
if not class_name.istitle():
raise NameError('名字首字母必须大写')
# 需求:定义类时必须明确父类
if len(bases) == 0:
raise CountError('父类必须明确')
# 控制文档注释不能为空
doc = namespace.get('__doc__')
if doc is None or len(doc) == 0 or len(doc.strip('\n ')) == 0:
raise TypeError("文档注释不能为空,且必须存在")
# super(MyMeta, cls).__init__(class_name, bases, namespace)
super().__init__(class_name, bases, namespace) # 继承type
# 自定义元类,重写call方法的目的:
# 1.被该元类控制的类生成对象,会调用元类的call方法
# 2.在call中的返回值就是创建的对象
# 3.在call中
# -- 通过object开辟空间产生对象
# -- 用被控制的类回调到自己的init方法完成名称空间的赋值
# -- 将修饰好的对象反馈给外界
def __call__(cls, *args, **kwargs):
print('call fn run')
# 创造一个空对象
obj = object.__new__(cls) # obj是Student这个类的对象
# 需求:所有通过该元类控制的类产生的对象都有meta_name该属性
obj.meta_name = cls.__name__
# obj.name = args[0]
# 执行__init__方法,完成对象的初始属性操作,进行名称空间的赋值
cls.__init__(obj, *args, **kwargs)
return obj
class Student(object, metaclass=MyMeta): # 如果不声明object,会报错:CountError('父类必须明确')
'''文档注释不能为空'''
def __init__(self, name, age):
print('init fn run')
self.name = name
self.age = age
stu = Student("Bob",18) # 会触发元类的__call__
print(stu)
print(stu.name)
print(stu.age)
# 问题:
# 1.继承是想获得父级的属性和方法,元类是要将类的创建于对象的创建加以控制
# 2.类的创建由元类的__init__方法控制
# -- 元类(class_name, bases, namespase) => 元类.__init__来完成实例化
# 3.类的对象的创建由元类的__call__方法控制
# -- 对象产生是需要开辟空间,在__call__中用object.__new__()来完成的
class Student(object, metaclass=MyMeta):
'''文档注释不能为空'''
pass
# class Student: <=> type(class_name, bases, namespace)
四、单例模式的实现
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
4.1、模块导入实现
Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc
文件,当第二次导入时,就会直接加载 .pyc
文件,而不会再次执行模块代码。
SingleModule文件
class Songs():
pass
# s1 = Songs()
# s2 = Songs()
# print(s1==s2) # False,实例化的对象地址不一样
# 对外提供的对象
song = Songs()
test1文件
def t():
from 模块导入实现.singleModule import song
print(song)
test文件
# 如果需要song对象,任何文件在任何地方通过导入拿到的是唯一对象
from 模块导入实现.singleModule import song
print(song)
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>
from 模块导入实现.singleModule import song
print(song)
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>
from 模块导入实现.singleModule import song
print(song)
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>
from 模块导入实现.test1 import t
t()
# <模块导入实现.singleModule.Songs object at 0x00000009EC7A7828>
4.2、约定用类方法来获取
class Songs():
__instance = None
@classmethod
def getInstance(cls):
# 对象没有就创建并返回,存在时直接返回
if cls.__instance == None:
cls.__instance = cls()
return cls.__instance
# 约定别用 类名() 来实例化对象,用类方法来获取唯一对象
s1 = Songs.getInstance()
s2 = Songs.getInstance()
print(s1==s2) # True,内存地址一样
4.3、重写__new__方法实现
'''
class Songs:
# 类一旦重写__new__方法,该类的实例化由__new__来控制
def __init__(self):
print('init')
def __new__(cls, *args, **kwargs):
print(args)
print(kwargs)
print('new')
s1 = Songs(1, 2)
s2 = Songs(3, 4)
print(s1, s2) # __new__没设返回值,此处为空值
'''
class Songs:
__instance = None
def __new__(cls, song_name, *args, **kwargs):
if cls.__instance == None:
cls.__instance = object.__new__(cls)
cls.__instance.song_name = song_name
return cls.__instance
def change_song(self, song_name):
self.song_name = song_name
s1 = Songs("Mylove")
s2 = Songs("倾城")
print(s1==s2) # True
print(s1.song_name,s2.song_name) # Mylove Mylove
s2.change_song("倾城")
print(s1.song_name,s2.song_name) # 倾城 倾城
4.4、装饰器完成
def outer(cls):
__instance = None
def inner(*args,**kwargs):
nonlocal __instance
if __instance == None:
__instance = cls(*args,**kwargs)
return __instance
return inner
@outer # Songs = outer(Songs)
class Songs:
pass
s1 = Songs()
s2 = Songs()
print(s1 is s2) # True,内存地址相同
还有一种就是不传参时就是同一个对象,如果传了其他参数就另外生成一个对象
settings文件:
IP = '1.1.1.1'
PORT = 3306
单例实现文件:
from settings import IP, PORT
def Singleton(cls):
__instance = cls(IP, PORT)
def inner(*args, **kwargs):
if args or kwargs:
obj = cls(*args, **kwargs)
return obj
return __instance
return inner
@Singleton
class MySQL:
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySQL()
obj2 = MySQL()
obj3 = MySQL()
print(obj1) # <__main__.MySQL object at 0x000000838F7A6710>
print(obj2) # <__main__.MySQL object at 0x000000838F7A6710>
print(obj3) # <__main__.MySQL object at 0x000000838F7A6710>
4.5、自定义元类实现
class SingleMeta(type):
__instance = None
def __call__(cls, *args, **kwargs):
if SingleMeta.__instance == None:
SingleMeta.__instance = object.__new__(cls)
cls.__init__(SingleMeta.__instance,*args,**kwargs)
return SingleMeta.__instance
class Songs(metaclass=SingleMeta):
def __init__(self):
pass
s1 = Songs()
s2 = Songs()
print(s1 is s2) # True,地址一样
# 从配置文件导入配置
settings文件
IP = '1.1.1.1'
PORT = 3306
# 元类实现单例
from settings import IP, PORT
class Mymeta(type):
# self 就是MySQL这个类
def __init__(self, class_name, class_bases, class_dic):
self.__instance = self(IP,PORT)
def __call__(self, *args, **kwargs):
if args or kwargs:
obj = self.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
else:
return self.__instance
class MySQL(metaclass=Mymeta):
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySQL() # <__main__.MySQL object at 0x0000000C02800B00>
obj2 = MySQL() # <__main__.MySQL object at 0x0000000C02800B00>
obj3 = MySQL() # <__main__.MySQL object at 0x0000000C02800B00>
print(obj1)
print(obj2)
print(obj3)