面向对象4
目录- 面向对象的魔法方法
- 基于魔法方法的笔试题
- 元类简介
- 创建类的两种方式
- 元类定制类的产生行为
- 元类定制对象的产生行为
- 魔法方法之双下new方法
- 设计模式简介及单例模式
面向对象的魔法方法
1.魔法方法的介绍
1.1其实就是类中定义的对双下方法的一个别名
1.2 之所以会叫魔法方法是因为这些方法都是到达某个条件自动触发,无需调用
例如:init__方法,在实例化对象的时候,类加括号,在括号里加一些数据的时候,它会自动调用这个方法
2.魔法方法的学习
2.1代码演练
class MyClass(object):
def __init__(self, name):
# print('__init__方法')
self.name = name
def __str__(self):
"""
对象被执行打印操作时候会自动触发
该方法必须返回一个字符串
返回什么字符串打印对象之后就展示什么字符串
"""
# print('__str__方法')
return ''
def __call__(self, *args, **kwargs):
# print('__call__方法')
# print(args)
# print(kwargs)
pass
def __getattr__(self, item):
"""
当对象获取一个不存在的属性名时,自动触发
该方法返回什么,对象获取不存在的属性名就会得到什么
形参item就是对象想要获取的不存在的属性名
"""
print('__getattr__方法')
return 123
def __setattr__(self, key, value):
"""
对象操作属性值的时候自动触发
有 对象.属性 = 属性值
"""
print('__setattr__') # __setattr__
print(key) # name
print(value) # tom
def __del__(self):
"""对象在被删除(主动 被动)的时候自动触发"""
# print('__del__')
pass
def __getattribute__(self, item):
"""
对象获取属性的时候自动触发 无论这个属性在不在
当类中既有__getattr__又有__getattribute__的时候 只会走后者
"""
return super().__getattribute__(item) # 简便写法
def __enter__(self):
"""对象被with语法执行的时候自动触发 该方法放回什么 as关键字 后的f就能得到什么 """
print('__enter__')
def __exit__(self, exc_type, exc_val, exc_tb):
"""对象被with语法执行并执行完with子代码之后 自动触发"""
print('__exit__')
obj = MyClass('jason')
# obj(1, 2, 3, name= 'kerry')
print(obj)
# obj.name = 'tom'
print(obj.age)
print(obj.__dict__)
with obj as f:
print(f)
2.2文字追加说明
1.双下init方法的结果:<__main__.MyClass object at 0x000001E4FD544B80>
2.双下 str方法的结果:TypeError: __str__ returned non-string (type NoneType)
双下 str在对象执行打印操作的时候就会自动触发,并且必须要放回一个字符串数据,否则报错
这个方法的目的是为了在我们操作打印的时候,可以做一些提示信息,并做一些更改
3.双下call方法,在类里面定义一个双下call,这个类在将来产生的对象,就会有加括号调用的功能
如果不写的话,就不能使用加括号调用
然后触发双下call的运行,并且还可以加参数
那么这个对象既然拥有的对象的功能还拥有了函数的功能
4.双下getattr方法,当对象获取一个不存在的属性名时,自动触发
如果放回一个 123的时候。那么在这个属性名不存在的时候,它会除非返回123,就是在不存在的时候返回是什么,这个对象.属性名
就会得到什么
5.双下 setattr方法,对象操作属性值的时候自动触发
6.双下 del方法,对象在被删除(主动 被动)的时候自动触发
在对象执行的时候就会自动触发
在整个运行结束的时候,整个空间所有的东西会被删除清空,那么这个时候也会被触发
7.双下getattribule,对象获取属性的时候时自动触发 无论这个属性存不存在
当类中既有双下getattr又有双下getattribute的时候 只会走后者
8.双下enter方法,对象被with语法执行的时候自动触发 该方法放回什么 as关键字 后的f就能得到什么
9.双下exit方法,对象被with语法执行并执行完with子代码之后 自动触发
2.3额外补充知识
额外补充:
print(obj.__dict__) # {}
obj.name(当对象获取一个不存字的属性名时,自动触发)的时候不存在,但是我们明明在双下init中存在name,这是为什么呢??
首先,我们可以使用的双下的那些方法是因为继承的父类里面的双下方法
然后我们在自己的类里面写了双下方法,那么就不会执行父类里面的双下方法了,现在我们知识打印了以下,并没有讲数据放入对象的独有空间里面,所以就是没有
那么现在就是,我们把原来的方法重新写了,不能再使用到原来的方法了,子类继承了父类,又把里面的方法重新写了,覆盖了父类的方法
如果想再次使用父类里面的原来的那个方法那么就使用:super().__setattr__(key, value) 将该有的东西传入,self可以不传
基于魔法方法的面试题
'''补全以下代码,执行之后不会报错'''
class Context:
# 补充的代码
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def do_something(self):
pass
with Context() as f: # Context()是一个对象,对象放到类里面,那么就靠考虑它会干什么事情
f.do_something()
"""自定义字典类型并让字典通过句点符的方式操作键值对"""
class MyDict(dict):
def __setattr__(self, key, value):
self[key] = value
def __getattr__(self, item):
return self.get(item)
obj = MyDict()
obj.name = 'jason'
obj.pwd = 18
obj.hobby = 'read'
# print(obj)
print(obj.name)
print(obj.pwd)
print(obj.hobby)
print(obj)
# print(obj) # 字典存储的数据 {}
# print(obj.__dict__) # 字典对象名称空间 {'name': 'jason'}
元类简介
s1 = '哈哈哈 今天下午终于可以敲代码了!!!'
l2 = [60, 80, 100, 120, 150, 200]
d = {'name': '死给我看', 'age': 18}
print(type(s1)) # <class 'str'>
print(type(l2)) # <class 'list'>
print(type(d)) # <class 'dict'>
"""
基础阶段我们使用type来查找数据的数据类型
但是学了面向对象之后 发现查看的不是数据类型 而是数据所属的类
我们定义的数据类型 其实本质还是通过各个类产生了对象
class str:
pass
h = 'hello' 等于 str('hello')
我们也可以理解为type用于查看产生当前对象的类是谁
"""
class MyClass:
pass
obj = MyClass()
print(type(obj)) # 查看产生对象obj的类:<class '__main__.MyClass'>
print(type(MyClass)) # 查看产生对象MyClass的类:<class 'type'>
"""
通过上述推导 得出结论 自定义的类都是由type类产生的
我们将产生类的类称之为 '元类'
"""
创建类的两种方式
# 方式1:使用关键字class
class Teacher:
school_name = '老女儿'
def func1(self):pass
print(Teacher)
print(Teacher.__dict__)
# 方式2:利用元类type type(类名,类的父类,类的名称空间)
cls = type('Student', (object,), {'name':'jason'})
print(cls)
print(cls.__dict__)
"""
了解知识:名称空间的产生
1.手动写键值对
针对绑定方法不好定义
2.内置方法exec
能够运行字符串类型的代码并产生名称空间
"""
元类定制类的产生行为
class MyMetaClass(type):
pass
"""只有继承了type的类才可以称之为是元类"""
class MyClass(metaclass=MyMetaClass):
pass
'''
metaclass是使用元类的关键字
如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass去声明
MyClass是由MyMetaClass负责产生的
类中的__init__用于实例化对象
元类中__init__用于实例化类
'''
class MyMetaClass(type):
# def __init__(self): # 报错,现在继承的是type,type里面有__init__,这个时候其实已经把元类中的__init__给替换掉了,元类里的__init__就不能在使用了,只要重写了父类,在最后都得调用回去,super().__init__(what, bases, dict)
def __init__(self,what, bases=None, dict=None):
# print('别磕睡')
# print('what', what) 类名 MyClass
# print('bases', bases) 类的父类
# print('dict', dict) 类的名称空间
if not what.istitle():
# print('首字母必须大写')
raise Exception('首字母必须大写')
super().__init__(what, bases, dict)
"""只有继承了type的类才可以称之为是元类"""
# class Myclass(metaclass=MyMetaClass):
# pass
class aaa(metaclass=MyMetaClass):
pass
元类定制对象的产生行为
元类不仅可以控制类的产生过程,还可以控制对象的产生
1.对象加括号执行产生该对象类里面的双下call
2.类加括号执行产生该类的元类里面的双下call
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
print('__call__')
if args:
raise Exception('必须用关键字参数传参')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyMetaClass):
def __init__(self, name, age):
self.name = name
self.age = age
obj = MyClass('jason', 18)
obj = MyClass(name='jason', age=38)
'''
参数是怎么传入进去的?
参数给予双下call,然后在双下call中会调用双下init
'''
"""
总结:
如果我们想高度定制对象的产生过程
可以操作元类里面的__call__
如果我们想高度定制类的产生过程
可以操作元类里面的__init__
"""
魔法方式之双下new
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__可以产生空对象
"""
设计模式简介
1.如何理解设计模式
在IT行业有很多前人通过大量的验证创建出来解决一些问题的固定高效方法
2.设计模式(23种)
工厂模式(Factory Pattern)
抽象工厂模式(Abstract Factory Pattern)
单例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
工厂模式(Factory Pattern)
抽象工厂模式(Abstract Factory Pattern)
单例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
责任链模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解释器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
备忘录模式(Memento Pattern)
观察者模式(Observer Pattern)
状态模式(State Pattern)
空对象模式(Null Object Pattern)
策略模式(Strategy Pattern)
模板模式(Template Pattern)
访问者模式(Visitor Pattern)
MVC 模式(MVC Pattern)
业务代表模式(Business Delegate Pattern)
组合实体模式(Composite Entity Pattern)
数据访问对象模式(Data Access Object Pattern)
前端控制器模式(Front Controller Pattern)
拦截过滤器模式(Intercepting Filter Pattern)
服务定位器模式(Service Locator Pattern)
传输对象模式(Transfer Object Pattern)
3.设计模式分类
创建型 结构型 行为型
4.设计模式之单例模式
类加括号调用多次只允许产生一个对象
class MyClass:
pass
obj1 = MyClass()
obj2 = MyClass()
obj3 = MyClass()
print(id(obj1), id(obj2), id(obj3)) # 2263190030944 2263190494080 2263189844464
'''
正常情况下 类名只要加括号实例化产生对象 执行几次就会产生几个不同的对象
'''
'''
有时候我们不希望类频繁的产生不同的对象
类中有很多很好用的方法 程序很多地方都需要使用(通过对象调用)
如果产生的地方特别多 那么会浪费一定的内存空间 所以需要使用单例
'''
方式1:使用元类干预对象的创建过程
class MyMeTaClass(type):
# 记录类是否已经创建了对象
instance = None
def __call__(self, *args, **kwargs):
if self.instance:
return self.instance
# 获取空对象
obj = super().__call__(*args, **kwargs)
# 保存对象
self.instance = obj
# 返回空对象
return obj
class Single(metaclass=MyMeTaClass):
def __init__(self, name):
self.name = name
obj1 = Single('jason')
obj2 = Single('kevin')
obj3 = Single('tony')
print(id(obj1), id(obj2), id(obj3))
print(obj1.name)
print(obj2.name)
print(obj3.name)
标签:__,obj,对象,Pattern,self,面向对象,print
From: https://www.cnblogs.com/zjl248/p/16871167.html