1.面向对象的魔法方法
魔法方法:类中定义的双下方法都称为魔法方法
使用方法:不需要人为调用,在特定条件下自动触发运行
eg:__init__是创建对象之后自动触发给对象添加独有数据的方法
1.__init__:添加对象独有数据的方法,对象添加数据时自动触发
class A:
def __init__(self, name):
self.name = name
print('__init__')
obj = A('max')
2.__str__:对象在被执行打印操作的时候自动触发,用法是在类体代码中定义__str__(self),先生成一个对象,在print(对象名),就可以自动触发打印返回值
class A:
def __str__(self):
'''对象在被执行打印操作的时候自动触发,并且返回什么执行结果就是什么'''
return '哈哈哈'
a = A()
print(a)
"""
返回值只能是字符串,非字符串数据类型会报错
"""
class A:
def __str__(self):
return 123
a = A()
print(a)
"""
maximum recursion depth:最大递归深度;此代码报错原因是print(a)会自动触发__str__并且返回f'{self}说:哈哈哈',此时print(a就相当于print(f'{self}说:哈哈哈'),print里面反复递归对象a,直到达到最大递归深度报错,所以返回的字符串中不能有对象本身
"""
class A:
def __str__(self):
return f'{self}说:哈哈哈'
a = A()
print(a)
"""
这个特性也可以用来分辨对象具体是谁, 返回的字符串中不能有对象本身,但可以点名字来看具体是哪个对象
"""
class A:
def __init__(self, name):
self.name = name
def __str__(self):
return f'对象{self.name}'
a = A('max')
print(a)
3.__call__:对象加括号调用,该方法返回什么对象调用的返回值就是什么
class A:
def __init__(self, name):
self.name = name
def __str__(self):
return f'对象{self.name}'
def __call__(self, *args, **kwargs):
print('__call__')
return '__call__的返回值'
a = A('max')
print(a())
"""
对象类似于函数名,可以在括号内加上位置参数和关键字参数,打印args和kwargs可以看到他们分别收集了括号内的位置参数和关键字参数,对象名加括号可以调用__call__,print(res)还可以接受到__call__的返回值
"""
class A:
def __init__(self, name):
self.name = name
def __str__(self):
return f'对象{self.name}'
def __call__(self, *args, **kwargs):
print(args, kwargs)
return '__call__的返回值'
a = A('max')
res = a(123, '123', name='max')
print(res)
4.__getattr__:对象点的名字不存在时自动触发
class A:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
return '很抱歉你找的名字不存在'
a = A('max')
print(a.name)
print(a.age)
5.__getattribute__:对象在查找名字时会自动触发,不管名字是否存在,并且只要有__getattribute__在,__getattr__会自动失效,例题中a.age不存在但是但是依然返回了'嘿嘿嘿',是因为__getattribute__作用将__getattr__覆盖
class A:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
return '很抱歉你找的名字不存在'
def __getattribute__(self, item):
return '嘿嘿嘿'
a = A('max')
print(a.name)
print(a.age)
"""
打印__getattribute__括号内的item后发现是一个对象名点的名字
"""
class A:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
return '很抱歉你找的名字不存在'
def __getattribute__(self, item):
print(item)
return '嘿嘿嘿'
a = A('max')
print(a.age)
6.__getitem__:对象用中括号取值并且值不存在的时候触发:对象['属性']值不存在时触发。
6.__setattr__:当对象执行对象名.名字=值的时候(对象定义独有的数据或修改数据)就会自动触发
class A:
def __init__(self, name):
'''第一步:self此时就是a,满足对象名.名字=值,所以会执行一次__setattr__'''
self.name = name
def __setattr__(self, key, value):
print('__setattr__')
a = A('max')
a.name = 'jack'
'''第二步,再执行一次__setattr__'''
"""
__setattr__(self, key, value)中的key和value分别指name和值max
"""
class A:
def __init__(self, name):
self.name = name
def __setattr__(self, key, value):
print('__setattr__')
print(key, value)
class Student:
def __init__(self, name):
self.name = name
a = A('max')
用__setattr__修改属性名:
obj1 = Student('max')
print(obj1.__dict__)
obj1.__setattr__('name', 'jason')
print(obj1.__dict__)
7.__setitem__:对象['属性']=值的时候触发。
8.__enter__:当对象跟在with后面,被当做上下文管理操作开始会自动触发__enter__。
__exit__:当对象参与with上下文管理语法运行完毕后自动触发(with子代码运行完毕)
'''__enter__方法一般和__exit__连用'''
class A:
def __init__(self, name):
self.name = name
def __enter__(self):
print('enter')
return 123
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit')
return 234
a = A('max')
with a as f:
print(f)
由此得出,f拿到的是__enter__的返回值
2.魔法方法笔试题
1.补全下列代码使得运行不报错即可:
class Context:
pass
with Context() as f:
f.do_something()
"""
分析:有with 对象,说明with参与上下文管理,必须要有__enter__方法,结束时必须要有__exit__(这两者一般会联合使用)。此外do_something()还没有定义,还需要定义一个功能do_something(),调用时才不会报错
"""
class Context:
def do_something(self):
pass
def __enter__(self):
return self
'''此行代码执行完毕需要一个f来点do_dometging(),我们一般用的最多的就是对象点的方式,所以此处需要返回对象本身也就是self。正好上节说道f拿到的是__enter__的返回值,所以旭阳将对象本身返回给f,再用对象点名字。所以return后面要跟self'''
def __exit__(self, exc_type, exc_val, exc_tb):
pass
with Context() as f:
f.do_something()
2.请定义一个字典,该字典可以通过点的方式来取值
"""
我们之前学过点的方式取值,但是之前是通过对象点名字来取名称空间中的名字,而本题要求字典点的方式来取字典中的值,两者有本质区别
"""
'''1.定义一个类,该类可以派生字典里所有的方法'''
class Mydict(dict):
'''2.由于添加键值对要通过点的方式来进行,那么可以联想到魔法方法中的__setattr__方法,该方法在对象名点名字=值时调用,且此时的key就指name,value指'max' '''
def __setattr__(self, key, value):
'''3.此时self就是obj,也就是构建的临时字典,通过按K取值的方式定义好取值方式'''
self[key] = value
'''4.定义好了添加键值对,此时再考虑取值。__getattr__特点是遇到名称空间中找不到的名字会自动触发,由于对象、类名称空间中都为空,所以查找名字肯定会自动触发__getattr__,且item是对象后面点的名字'''
def __getattr__(self, item):
'''5.返回一个通过item取到的值'''
return obj.get(item)
obj = Mydict()
obj.name = 'max'
print(obj.name)
3.元类简介
元类概念推导:
步骤1:如何查看数据的数据类型
s1 = 'hello world'
l1 = [11, 22, 33, 44]
d1 = {'name': 'jason', 'pwd': 123}
t1 = (11, 22, 33, 44)
print(type(s1))
print(type(l1))
print(type(d1))
print(type(t1))
步骤2:type不仅可以查看数据类型,也可以查看产生对象的类名
class Group:
pass
obj = Group()
print(type(obj))
步骤3:type(对象名)结果是类名,那么type(类名)的结果是什么呢?
class Group:
pass
obj = Group()
print(type(obj))
print(type(Group))
type的父类依然是type
print(type(type))
"""
我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type
"""
4.创建类的两种方式
方式1:通过关键字class创建
class Teacher:
name = 'jason'
age = 18
a = Teacher()
print(a.name, a.age)
方式2:通过关键字type创建,语法结构:type(类名, 类的父类, 类的名称空间)
Teacher = type('Teacher', (), {'name': 'jason', 'age': 18})
a = Teacher()
print(a.name, a.age)
"""
也可以继承父类中的名字(如果只有一个父类后面需加逗号,同元组)
"""
class Person:
def work(self):
print('人不能不上班')
Teacher = type('Teacher', (Person,), {'name': 'jason', 'age': 18})
a = Teacher()
print(a.work())
5.元类参与类的产生行为
"""
在某一步设定一些条件,来干涉类产生的行为,对象是由类加括号产生的,类是由元类加括号产生的。一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类
对象是由类名加括号产生的 __init__
类是由元类加括号产生的 __init__
"""
class MyMetaClass(type):
def __init__(cls, what, bases=None, dict=None):
print('what', what)
print('bases', bases)
print('dict', dict)
super().__init__(what, bases, dict)
'''通过metaclass=MyMetaClass来继承元类'''
class Student(metaclass=MyMetaClass):
name = 'max'
"""
通过依次打印what, bases, dict得知,what是继承了MyMetaClass的类,dict是类Student的名称空间。元类产生类的行为其实也就是:Student=MyMetaClass('Student',object,{})
"""
class MyMetaClass(type):
def __init__(cls, what, bases=None, dict=None):
if not what.istitle():
raise TypeError('首字母必须大写')
super().__init__(what, bases, dict)
class student(metaclass=MyMetaClass):
name = 'max'
class Student(metaclass=MyMetaClass):
name = 'max'
6.元类参与对象的产生行为
1.对象加括号会执行产生该对象类里面的__call__
推导得出:类加括号会执行产生该类的类里面的__call__
2.给对象添加一些独有数据,首先是传到了元类里面的__call__,位置参数传给了args,关键字参数传给了kwargs,其次再交给__init__
"""
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
print('__call__')
print(args, kwargs)
super().__call__(*args, **kwargs)
class Student(metaclass=MyMetaClass):
def __init__(self, name, age, gender):
print('__init__')
self.name = name
self.age = age
self.gender = gender
obj = Student('max', 25, 'male')
'''
执行结果:
__call__
('max', 25, 'male') {}
__init__
'''
要求:定义一个类,声称对象只能上传关键字参数
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
if args:
raise TypeError('必须要传关键词参数哦')
'''super语句前面一定要加上return,不加会报错'''
return super().__call__(*args, **kwargs)
class Student(metaclass=MyMetaClass):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
# obj = Student('max', 25, 'male') # TypeError: 必须要传关键词参数哦
obj = Student(name='max', age=25, gender='male')
print(obj.__dict__) # {'name': 'max', 'age': 25, 'gender': 'male'}
"""
分析:为什么要用类Student继承元类MyMetaClass呢?
因为对象产生的过程中首先把参数传入MyMetaClass的__call__,只有用Student继承元类MyMetaClass才能介入对象的生成过程。
如果args布尔值为True说明上传了关键字参数,这种情况下主动报错,报错情况下不会执行,只有args布尔值为Flase时才会执行super语句
"""
7.魔法方法之双下new
"""
产生一个对象的步骤:
1.产生一个空对象(骨架)
2.调用__init__给空对象添加该对象独有的数据(血肉)
3.返回创建好的对象
4.继承自object的新式类才有__new__
5.__new__至少要有一个参数cls,代表当前类,此参数在实例化时由Python解释器自动识别
6.__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例
注:__init__有一个参数self,这个就是__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
7.如果__new__创建的是当前类的实例,会自动调用__init__函数,通过return语句里面调用的__new__函数的第一个参数是 cls 来保证是当前类实例,如果是其他类的类名,那么实际创建返回的就是其他类的实例,其实就不会调用当前类的__init__函数,也不会调用其他类的__init__函数。
更多查看:https://www.cnblogs.com/zhaoyuanshi/p/16130508.html
"""
上述步骤可以用代码模拟出来:
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
obj = self.__new__(self)
self.__init__(obj,*args, **kwargs)
return obj
class Student(metaclass=MyMetaClass):
def __init__(self, name):
self.name = name
obj = Student('jason')
print(obj.name)
"""
__new__可以产生空对象
"""
"""
__init__和__new__的区别:
1.__init__和__new__都是类创建实例时调用,__init__是初始化实例时调用,__new__是创建实例时调用,所以__init__在__new__之后调用:
class Person(object):
def __init__(self, name, age):
print('__init__called')
self.name = name
self.age = age
def __new__(cls, name, age):
print('__new__called')
return super().__new__(cls)
per1 = Person('max', 26)
执行结果:
__new__called
__init__called
2.__new__ 通常用于控制生成一个新实例的过程。它是类级别的方法。
__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作
3.__new__是一个静态方法,而__init__是一个实例方法
4.__new__会返回一个cls的实例,__init__什么都不返回
"""
8.设计模式简介
1.设计模式:前人通过大量的验证创建出来解决一些问题的固定高效方法
2.IT行业目前有23种设计模式。分类:创建型、结构型、行为型
3.单例模式
类加括号无论执行多少次永远只会产生一个对象
目的:
当类中有很多非常强大的方法 我们在程序中很多地方都需要使用
如果不做单例 会产生很多无用的对象浪费存储空间
我们想着使用单例模式 整个程序就用一个对象
9.单例模式实现
方式1:类中存储单例
class C1:
__isinstance = None
def __init__(self, name, age):
self.name = name
self.age = age
'''通过这种方法产生一个固定的对象,并且只能调用此种方式产生'''
@classmethod
def singleton(cls):
if not cls.__isinstance:
cls.__isinstance = cls('max', 123)
return cls.__isinstance
obj1 = C1.singleton()
obj2 = C1.singleton()
print(id(obj1), id(obj2))
"""
此种方法也可以产生正常的对象,不调用singleton方法就可以正常产生
"""
方法2:通过模块来实现单例模式
md.py文件中代码:
class C1:
def __init__(self, name):
self.name = name
obj = C1('jason')
另一个py文件:
import md
print(id(md.obj))
print(id(md.obj))
方式3:元类控制单例类的产生
class MyType(type):
def __init__(cls, what, bases=None, dict=None):
cls.__instance = cls.__new__(cls)
cls.__init__(cls.__instance, 'jason', 18)
super().__init__(what, bases, dict)
def __call__(cls, *args, **kwargs):
if args or kwargs:
obj = cls.__new__(cls)
cls.__init__(obj, *args, **kwargs)
return obj
return cls.__instance
class Mysql(metaclass=MyType):
def __init__(self, name, age):
self.name = name
self.age = age
obj1 = Mysql()
obj2 = Mysql()
print(id(obj1), id(obj2))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南