首页 > 其他分享 >魔法方法与元类

魔法方法与元类

时间:2022-11-08 20:25:12浏览次数:52  
标签:__ name getattribute self 魔法 元类 print 方法 def

魔法方法与元类

目录

面向对象的魔法方法

魔法方法:类中定义的双下方法都称为魔法方法,这种方法需要人为调用,在特定的条件下回自动触发运行。
eg:__init__创建空对象之后自动触发给对象添加独有的数据
    
1.__init__
 	对象添加独有数据的时候自动触发
初始化创建好的对象,初始化的前提是:“给实例对象赋值”。
如果不定义__init__方法,系统会提供一个默认的__init__方法。
如果定义了带参的__init__方法,系统不创建默认的__init__方法。__init__只能返回None,一般不写return。

2.___str__
	对象被执行打印操作的时候自动触发,该方法返回的必须是字符串。

3.__call__
	对象加括号调用的时候自动触发。
    可调用对象:凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数callable。例如,函数、内置函数、类都属于可调用对象。
    在类中重新定义_call_方法,那么实例对象也将成为一个可调用对象,例:

class SalaryAcount:
    def __call__(self,salary):
        year=salary*12
        return dict(month=salary,year=year)
s=SalaryAcount()
print(s(10000))
# {'month': 10000, 'year': 120000}

4.__getattr__与__getattribute__
"""
__getattr__对象点不存在的名字的时候自动触发
__getattribute__对象点名字就会自动触发 有它的存在就不会执行__getattr__
"""
重载_getattr_方法对类及其实例未定义的属性有效。如果访问的属性存在,就不会调用_getattr_方法。这个属性的存在,包括类属性和实例属性。

class ClassA:
    x='a'
    def __init__(self):
        self.y='b'
    def __getattr__(self, item):
        return '报告老大,有人调用getattr方法,item:{}'.format(item)
a=ClassA()
print(a.x)# 输出结果a
print(a.y)# 输出结果b
print(a.z)# 输出结果,报告老大,有人调用getattr方法,item:z

通过类名直接调用类属性不会调用_getattr_和_getattribute_
通过实例化对象调用实例属性或者类属性都会首先调用_getattribute_,无论类存在与否
内置getattr和hasattr也会触发这个魔法方法_getattribute_

class ClassA:
    x='a'
    def __init__(self):
        self.y='b'
    def __getattribute__(self, item):
        return '__getattribute__'
print(ClassA.x)# a
a=ClassA()
print(a.x)# __getattribute__
print(a.y)# __getattribute__
print(a.z)# __getattribute__
在__getattribute__代码块中,再次执行属性的获取操作时,会再次触发__getattribute__方法的调用,代码将会陷入无限递归

class ClassA:
    x = 'a'
    def __getattribute__(self, item):
        print('__getattribute__')
        return self.item   
# 再次出现属性的获取操作,会再次触发__getattribute__的调用,相当于return self.__getattribute__(item)
a = ClassA()
a.x
  为了避免无限递归,应该把获取属性的方法 __getattribute__指向一个更高的超类,例如object。

class ClassA:
    x = 'a'                                   # 类属性
    def __getattribute__(self, item):
        print('__getattribute__')
        return super().__getattribute__(item)  
    # 或者使用return object.__getattribute__(self, item)
a = ClassA()
print(a.x)                                     
# 输出__getattribute__
  当同时定义__getattribute__和__getattr__时,__getattr__方法不会再被调用,除非显示调用__getattr__方法或引发AttributeError异常。 

class A():
    x = 1
    def __getattribute__(self, item):
        print('正在调用__getattribute__,属性{}'.format(item))
        if item != 'x':
            raise AttributeError
        return super().__getattribute__(item)
    def __getattr__(self, item):
        print('正在调用__getattr__属性{}'.format(item))
        return 'aa'
a = A()
print(a.x)
print(a.y)
总结:通过实例化对象调用实例属性或类属性的过程
首先调用getattribute方法,为避免无限递归循环,getattribute应当返回object.__getattribute(self,item)
object的getattribute方法会先查找实例的属性来查找返回,如果在实例的属性中未找到,则去类属性查找返回,如果类属性未找到,则调用getattr方法。

6.__setattr__
	给对象添加或者修改数据的时候自动触发  对象.名字 = 值
class Person():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __setattr__(self, key, value):
        self.__dict__[key]=value  
        #此处不能写成self.key = value,会导致无线循环
p=Person('jason',28)
p.gender='man'
print(p.__dict__)
# {'name': 'jason', 'age': 28, 'gender': 'man'}

会执行三次print函数,是因为在__init__方法中,对象A初始化时给属性name和age赋值时,触发了__setattr__方法。 使用该方法是同样需要十分小心避免无限循环陷阱
      如果定义__setattr__方法的同时定义了__getattribute__方法,那么在修改__dict__字典中的键值对时, 由于调用了self.__dict__属性,同样会触发__getattribute__方法,使用时应格外小心
class Person():
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __setattr__(self, key, value):
        print('执行__setattr__')
        self.__dict__[key] = value
    def __getattribute__(self, item):
        print('执行__getattribute__')
        print('item:{}'.format(item))           
        #此处的item是__dict__
        return super().__getattribute__(item)
p = Person('kuangfeng',20)
p.gender = 'man'
print(p.__dict__)

7. __enter__与__exit__
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量

	__exit__(self, exc_type, exc_val, exc_tb)
exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
     如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

8.__exit__
	with上下文管理语法运行完毕之后自动触发(子代码结束)

基于魔法方法的笔试题

1.补全下列代码使得运行不报错即可
class Context:
    pass
with Context() as f:
    f.do_something()
解:
class Context:
    def do_something(self):
        pass
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Context() as f:
    f.do_something()

2.自定义字典类型并让字典能够通过句点符的方式操作键值对
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.__dict__)
# print(obj)# {'name': 'jason', 'pwd': 18, 'hobby': 'read'}
# print(obj.name)# jason
# print(obj.pwd)# 18
# print(obj.hobby)# read
# print(obj.__dict__)# 字典的名称空间 {}
# print(type(obj))# <class '__main__.MyDict'>

元类简介

一切源于一句话:python中一切皆为对象。让我们先定义一个类,然后逐步分析

class Teacher(object):
    school='开心'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the "开心" to learn Python' %self.name)
"""
所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类Teacher得到的
"""
t1 = Teacher('lili', 18)
print(type(t1))  # 查看对象t1的类是<class '__main__.Teacher'>
"""
如果一切皆为对象,那么类Teacher本质也是一个对象,既然所有的对象都是调用类得到的,那么Teacher必然也是调用了一个类得到的,这个类称为元类。
"""
print(type(Teacher))
# 结果为<class 'type'>,证明是调用了type这个元类而产生的Teacher,即默认的元类为type

image

创建类的两种方式

# 方式1:使用关键字class
class Teacher:
    school_name = '老女儿'
    def func1(self):pass
print(Teacher)
# <class '__main__.Teacher'>

print(Teacher.__dict__)
# {'__module__': '__main__', 'school_name': '老女儿', 'func1': <function Teacher.func1 at 0x000001720D67B4C0>, '__dict__': <attribute '__dict__' of 'Teacher' objects>, '__weakref__': <attribute '__weakref__' of 'Teacher' objects>, '__doc__': None}

# 方式2:利用元类type  type(类名,类的父类,类的名称空间)
cls = type('Student', (object,), {'name':'jason'})
print(cls)
# <class '__main__.Student'>

print(cls.__dict__)
# {'name': 'jason', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

"""
了解知识:名称空间的产生
1.手动写键值对
    针对绑定方法不好定义
2.内置方法exec
    能够运行字符串类型的代码并产生名称空间
"""

元类定制类的产生行为

"""
推导
	对象是由类名加括号产生的  	__init__
	类是由元类加括号产生的		__init__
"""
"""所有的类必须首字母大写 否则无法产生"""
# 1.自定义元类:继承type的类也称之为元类
class MyMetaClass(type):
    def __init__(self, what, bases=None, dict=None):
        # print('what', what)
        # print('bases', bases)
        # print('dict', dict)
        if not what.istitle():
            raise TypeError('你是不是python程序员 懂不懂规矩 类名首字母应该大写啊!!!')
        super().__init__(what, bases, dict)


# 2.指定类的元类:利用关键字metaclass指定类的元类
class myclass(metaclass=MyMetaClass):
    desc = '元类其实很有趣 就是有点绕'

class Student(metaclass=MyMetaClass):
    info = '我是学生 我很听话'
print(Student)
print(Student.__dict__)

元类定制对象的产生行为

"""
推导
	对象加括号会执行产生该对象类里面的   __call__
	类加括号会执行产生该类的类里面的	__call__
"""
"""给对象添加独有数据的时候 必须采用关键字参数传参"""

class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        # 1.产生一个空对象(骨架)
        # 2.调用__init__给对象添加独有的数据(血肉)
        # 3.返回创建好的对象
        # print(args)  # ('jason', 18, 'male')
        # print(kwargs)  # {}
        if args:
            raise TypeError("你怎么回事 Jason要求对象的独有数据必须按照关键字参数传参 我看你是不想干了!!!")
        return 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('jason', 18, 'male')
obj = Student(name='jason',age= 18,gender= 'male')
print(obj.__dict__)

魔法方法之双下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.创建型模式,共五种:
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

2.结构型模式,共七种:
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

3.行为型模式,共十一种:
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

设计模式的六大原则

开闭原则
开闭原则就是说对扩展开放,对修改关闭。
里氏代换原则
面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
依赖倒转原则
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
接口隔离原则
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。
迪米特法则(最少知道原则)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
合成复用原则
原则是尽量使用合成/聚合的方式,而不是使用继承。

单例模式

类加括号无论执行多少次永远只会产生一个对象
目的:
    当类中有很多非常强大的方法 我们在程序中很多地方都需要使用
    如果不做单例 会产生很多无用的对象浪费存储空间
    我们想着使用单例模式 整个程序就用一个对象

标签:__,name,getattribute,self,魔法,元类,print,方法,def
From: https://www.cnblogs.com/zhiliaowang/p/16871028.html

相关文章

  • 面向对象的魔法方法及元类
    魔法方法 类中定义的双下方法都可以叫做魔法方法,其特定情况下可以自动触发运行1.__init__对象添加独有数据的时候自动触发2.__str__对象执行打印操作时自动触发......
  • Python——面向对象(魔法方法、元类)
    Python——面向对象(魔法方法、元类)魔法方法'''魔法方法: 类中定义的双下方法都称为魔法方法 不需要人为调用在特定的条件下回自动触发运行 eg:__init__创建空......
  • 面向对象之魔法方法及元类
    昨日内容回顾类的派生的应用在需要对原有的方法进行个性化扩展时,需要应用到方法的派生。在进行派生时,需要先定位到需要派生的的功能,添加个性化的功能后,使用super调用......
  • 面向对象之魔法方法/元类
    面向对象的魔法方法classC1:name='张三丰'age=18def__init__(self,name):#对象添加数据或改变数据时自动触发print('__init__......
  • 面向对象的魔法方法、元类定制类与对象的产生行为
    今日内容回顾目录今日内容回顾面向对象的魔法方法魔法方法笔试题元类简介创键类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new设计模式简介面......
  • 系统调用捕获和分析—使用LKM方法添加系统调用
    本文为毕业设计过程中学习相关知识、动手实践记录下来的完整笔记,通过阅读本系列文章,您可以从零基础了解系统调用的底层原理并对系统调用进行拦截。由于本人能力有限,文章中可......
  • 系统调用捕获和分析—修改内核方法添加系统调用
    本文为毕业设计过程中学习相关知识、动手实践记录下来的完整笔记,通过阅读本系列文章,您可以从零基础了解系统调用的底层原理并对系统调用进行拦截。由于本人能力有限,文章中可......
  • day22- 同步方法
    同步方法死锁的概念互斥条件:一个资源每次只能被一个进程使用请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放不剥夺条件:进程已获得的资源,在未......
  • 7个存钱方法,5个存钱建议 |无痛存钱,告别月光族!(建议收藏)
    俗话说:不积跬步,无以至千里;不积小流,无以成江海。聚沙成塔,财富可以通过持之以恒地打理来积累。现在很多上班族都是月光族,我以前也是这样的,拿到工资就开始清空购物车,觉得赚......
  • 面向对象的魔法方法与元类
    面向对象的魔法方法与元类面向对象的魔法方法魔法方法就是在类中定义的双下方法,它不需要人为调用,在特定的条件下会自动触发运行1.__init__对象添加独有数据的时候会......