1.面向对象的魔法方法
魔法方法:类中定义的双下方法都称为魔法方法
使用方法:不需要认为调用,在特定条件下自动触发运行
eg:__init__是创建对象之后自动触发给对象添加独有数据的方法
1.__init__:添加对象独有数据的方法,对象添加数据时自动触发
class A:
def __init__(self, name):
self.name = name
print('__init__')
#类名加括号,给对象添加独有数据的方法
obj = A('max') # obj = A('max')
2.__str__:对象在被执行打印操作的时候自动触发,用法是在类体代码中定义__str__(self),先生成一个对象,在print(对象名),就可以自动触发打印返回值
class A:
def __str__(self):
'''对象在被执行打印操作的时候自动触发,并且返回什么执行结果就是什么'''
return '哈哈哈' # __str__子代码中不能用print,只能用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) # 错误信息:RecursionError: maximum recursion depth exceeded while calling a Python object
"""
这个特性也可以用来分辨对象具体是谁, 返回的字符串中不能有对象本身,但可以点名字来看具体是哪个对象
"""
class A:
def __init__(self, name):
self.name = name
def __str__(self):
return f'对象{self.name}'
a = A('max')
print(a) # 对象max
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()) # __call__ # __call__的返回值
"""
对象类似于函数名,可以在括号内加上位置参数和关键字参数,打印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') # (123, '123') {'name': 'max'}
print(res) #__call__的返回值
4.__getattr__:对象点的名字不存在时自动触发
class A:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
return '很抱歉你找的名字不存在'
a = A('max')
print(a.name) # max
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) # age 嘿嘿嘿
5.__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__
'''第二步,再执行一次__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)
a = A('max') # __setattr__ # name max
6.__enter__:当对象跟在with后面,被当做上下文管理操作开始会自动触发__enter__。
__exit__:当对象参与with上下文管理语法运行完毕后自动触发(with子代码运行完毕)
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) # enter 123 exit
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,再用对象点名字'''
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' # str()
l1 = [11, 22, 33, 44] # list()
d1 = {'name': 'jason', 'pwd': 123} # dict()
t1 = (11, 22, 33, 44) # tuple()
print(type(s1)) # <class 'str'>
print(type(l1)) # <class 'list'>
print(type(d1)) # <class 'dict'>
print(type(t1)) # <class 'tuple'>
步骤2:type不仅可以查看数据类型,也可以查看产生对象的类名
class Group:
pass
obj = Group()
print(type(obj)) # <class '__main__.Group'>
步骤3:type(对象名)结果是类名,那么type(类名)的结果是什么呢?
class Group:
pass
obj = Group()
print(type(obj)) # <class '__main__.Group'>
print(type(Group)) # <class 'type'>
type的父类依然是type
print(type(type)) # <class 'type'>
"""
结论:我们定义的类归根结底都是type产生的,所以我们把产生类的类(type)叫做元类
"""
4.创建类的两种方式
方式1:通过关键字class创建
class Teacher:
name = 'jason'
age = 18
a = Teacher()
print(a.name, a.age) # jason 18
方式2:通过关键字type创建,语法结构:type(类名, 类的父类, 类的名称空间)
Teacher = type('Teacher', (), {'name': 'jason', 'age': 18})
a = Teacher()
print(a.name, a.age) # jason 18
"""
也可以继承父类中的名字(如果只有一个父类后面需加逗号,同元组)
"""
class Person:
def work(self):
print('人不能不上班')
Teacher = type('Teacher', (Person,), {'name': 'jason', 'age': 18})
a = Teacher()
print(a.work()) # 人不能不上班 None
5.元类参与类的产生行为
"""
在某一步设定一些条件,来干涉类产生的行为,对象是由类加括号产生的,类是由元类加括号产生的。
对象是由类名加括号产生的 __init__
类是由元类加括号产生的 __init__
"""
class MyMetaClass(type):
def __init__(cls, what, bases=None, dict=None):
print('what', what) # what Student
print('bases', bases) # bases ()
print('dict', dict) # dict {'__module__': '__main__', '__qualname__': 'Student', 'name': 'max'}
super().__init__(what, bases, dict)
'''通过metaclass=MyMetaClass来继承元类'''
class Student(metaclass=MyMetaClass):
name = 'max'
"""
通过依次打印what, bases, dict得知,what是继承了MyMetaClass的类,dict是类Student的名称空间
"""
class MyMetaClass(type):
def __init__(cls, what, bases=None, dict=None):
# print('what', what) # what Student
# print('bases', bases) # bases ()
# print('dict', dict) # dict {'__module__': '__main__', '__qualname__': 'Student', 'name': 'max'}
if not what.istitle():
raise TypeError('首字母必须大写')
super().__init__(what, bases, dict)
class student(metaclass=MyMetaClass):
name = 'max' # TypeError: 首字母必须大写
class Student(metaclass=MyMetaClass):
name = 'max' # 可以正常创建
6.元类参与对象的产生行为
"""
对象加括号会执行产生该对象类里面的__call__
推导得出:类加括号会执行产生该类的类里面的__call__
"""
"""
给对象添加一些独有数据,首先是传到了元类里面的__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.返回创建好的对象
"""
上述步骤可以用代码模拟出来:
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
# 1.产生一个空对象(骨架)
obj = self.__new__(self)
# 2.调用__init__给对象添加独有的数据(血肉)
self.__init__(obj,*args, **kwargs)
# 3.返回创建好的对象
return obj
class Student(metaclass=MyMetaClass):
def __init__(self, name):
self.name = name
obj = Student('jason')
print(obj.name)
"""
__new__可以产生空对象
"""
8.设计模式简介
1.设计模式:前人通过大量的验证创建出来解决一些问题的固定高效方法
2.IT行业目前有23种设计模式。分类:创建型、结构型、行为型
3.单例模式
类加括号无论执行多少次永远只会产生一个对象
目的:
当类中有很多非常强大的方法 我们在程序中很多地方都需要使用
如果不做单例 会产生很多无用的对象浪费存储空间
我们想着使用单例模式 整个程序就用一个对象
标签:__,name,self,魔法,元类,面向对象,print,class,def
From: https://www.cnblogs.com/zkz0206/p/16871027.html