反射、魔法方法、元类

内容概要

  • 反射实战
  • 魔法方法
  • 魔法方法的实战
  • 元类
  • 创建元类的两种方式

内容详细

反射的实战案例

案例1

加载配置文件纯大写的配置(配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略 组织成字典)

import settings
new_dic = {}
for i in dir(settings):
    if i.isupper():
       new_dic[i] = getattr(settings,i)
print(new_dic)

总结:
1.dir(obj) :获取对象中可以调用的名字,以列表的形式返回
2.熟悉:在python中,一切皆对象(文件,模块...

案例2

模拟操作系统cmd终端执行用户命令

class Mywindows(object):
    def dir(self):
        print('dir获取当前目录下所有的文件名称')
    def ls(self):
        print('ls获取当前路径下所有的文件名称')
    def ipconfig(self):
        print('ipconfig获取当前计算机的网卡信息')
obj = Mywindows()
while True:
    cmd = input("请输入你的指令>>>:").strip()
    if hasattr(obj,cmd):
        name_cmd = getattr(obj,cmd)
        name_cmd()
    else:
        print(f"{cmd}没有这个指令哦")

面向对象的魔法方法

魔法方法其实就是类中定于的双下方法。被称为魔法方法的原因就是这些方法都是到达某个条件自动触发,不需要调用,例如:双下init方法在给对象设置独有的数据的时候,就会自动触发(实例化)。

1.str(self)

对象在被执行打印操作的时候会自动触发,该方法必须返回一个字符串,打印的时候,返回的是什么就打印什么(前提必须是字符串类型)

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
obj = Student("nana",18)
print(obj)  # <__main__.Student object at 0x0000026135F772B0>


class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __str__(self):
        print("__str__")
        # return f'这是对象:{self}'  # 报错,进行了递归
        return f'这是对象的名字:{self.name}'
obj = Student("nana",18)
print(obj)  # __str__  这是对象的名字:nana

2.call(self, *args, **kwargs)

只要对象加括号调用,就会触发该方法,括号里面可以写位置实参,关键字实参

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
obj = Student("nana",18)
obj()  # 报错 TypeError: 'Student' object is not callable

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __call__(self, *args, **kwargs):
        print("__call__")
        print(args)
        print(kwargs)
obj = Student("nana",18)
obj(1,2,name='xiao')  # __call__  (1, 2)  {'name': 'xiao'}

3.getattr(self, item)

只要当对象获取一个不存在的属性名,就会自动触发。该方法返回的是:return后面写的什么就返回什么,一般可以提示某个属性名不存在。形参item就是对象想要获取的不存在的属性名

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
obj = Student("nana",18)
print(obj.name)  # nana
print(obj.hobby)  # 报错 AttributeError: 'Student' object has no attribute 'hobby'

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __getattr__(self, item):
        print("__getattr__")
        return '您想要获取的属性名:%s不存在'%item

obj = Student("nana",18)
print(obj.name)  # nana
print(obj.hobby)  # __getattr__  您想要获取的属性名:hobby不存在

4. setattr(self, key, value)

当对象操作属性的时候会自动触发 eg:对象.属性名= 属性值

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __getattr__(self, item):
        print("__getattr__")
        return '您想要获取的属性名:%s不存在'%item

obj = Student("nana",18)
print(obj.name)  # nana
print(obj.age)  # 18


class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __getattr__(self, item):
        print("__getattr__")
        return '您想要获取的属性名:%s不存在'%item

    def __setattr__(self, key, value):
        print("__setattr__")
        print(key)
        print(value)

obj = Student("nana",18)  # __setattr__ name nana __setattr__ age 18
print(obj.__dict__)  # {}
print(obj.name)  # __setattr__ name nana __setattr__ age 18 __getattr__ 您想要获取的属性名:name不存在
print(obj.age) # __setattr__ name nana __setattr__ age 18 __getattr__ 您想要获取的属性名:age不存在
obj.name = 'xiao'  # __setattr__ name nana __setattr__ age 18 __setattr__ name xiao

ps:出现以上情况原因:
1.obj = Student("nana",18) >>>在__init__中也有对象操作属性的时候(self.name = name),说以会触发__setattr__
2.print(obj.__dict__),返回空字典,是因为Student类中,默认继承object父类,那object父类中本来就有__setattr__方法(对象操作属性),然而我们在重写了_setattr__方法,却没有任何操作,所以返回{}是理所应当的

解决:用super()继承父类
class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __getattr__(self, item):
        print("__getattr__")
        return '您想要获取的属性名:%s不存在'%item

    def __setattr__(self, key, value):
        super().__setattr__(key,value)

obj = Student("nana",18)
print(obj.name) # nana

5. del(self)

对象在被删除的时候会自动触发。删除包含主动删除和被动删除,主动删除就是执行删除操作,被动删除就是代码运行完毕,文件执行结束,释放内存,进行被动删除

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __del__(self):
        print("__del__")
obj = Student("nana",18)
del obj  # __del__

6.getattribute(self, item)

对象获取属性的时候自动触发,无论这个属性存不存在。当类中既有__getattr__,又有__getattribute__的时候,只会执行__getattribute__

class Student:
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __getattribute__(self, item):
        print("getattribute__")
obj = Student("nana",18)
print(obj.name)  # getattribute__  None
print(obj.hobby)  # getattribute__  None

class Student(object):
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __getattribute__(self, item):
        print("getattribute__")
        return super(Student, self).__getattribute__(item)
obj = Student("nana",18)
print(obj.name)  # getattribute__  nana
print(obj.hobby)  # getattribute__  

7.enter(self) 、exit(self, exc_type, exc_val, exc_tb)

enter(self):对象被with语法执行的时候自动触发,该方法返回什么,as关键字后面的变量名就能得到什么;exit(self, exc_type, exc_val, exc_tb):对象被with语法执行并运行完with子代码之后,自动触发

class Student(object):
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
obj = Student("nana",18)
with obj as f:
    pass       # 报错  AttributeError: __enter__

class Student(object):
    school = '清华'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __enter__(self):
        print('__enter__')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
obj = Student("nana",18)
with obj as f:
    print(f)    # >>>__enter__ <__main__.Student object at 0x00000152B76C7358>   __exit__

魔法方法练习

补全以下代码 执行之后不报错

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

解题

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:
    f.do_something()

元类

元类的简介

1.基础阶段我们使用type来查看数据的数据类型,学习了面向对象之后,发现查看的不是数据类型,而是数据类型所属的类

l1 = [1,2,3,4]
print(type(l1))  # <class 'list'>
s1 ={1,2,3,4}
print(type(s1))  # <class 'set'>

2.我们定义的数据类型 其实本质还是通过各个类产生对象

class str:
	pass
s1 = 'naan'  >>>相当于:s1 = str('naan')

3.type也可以被理解为用于查看产生当前对象的类是谁

class Myclass:
    pass
obj = Myclass()
print(type(obj))  # <class '__main__.Myclass'>
print(type(Myclass))  # <class 'type'>

总结:自定义的类都是由type类产生的,我们将产生类的类称之为元类

产生类的两种方式

1.class关键字
class 类名:
    类体代码
2.利用元类type
type(name, bases, dict) -> a new type
即:type(类名、类的父类、类的名称空间)
	type("Myclass",(),{})

学习元类其实就是掌握了类的产生过程,我们就可以在类的产生过程中高度定制化类的行为

类的基本使用

只有继承了type的类才可以称之为是元类;如果想要切换产生类的元类,不能使用继承,必须使用关键字metaclass声明

class MyMetaClass(type):
    pass

class Myclass(metaclass = MyMetaClass):
    pass

推论:类中的__init__用于实例化对象,元类中的__init__用于实例化类

class MyMetaClass(type):
    def __init__(self,what, bases=None, dict=None):
        print("你好")
        print('what', what)  # 类名 what Myclass
        print('bases', bases) # 类的父类 bases ()
        print('dict', dict) # 类的名称空间 dict {'__module__': '__main__', '__qualname__': 'Myclass'}
        super().__init__(what, bases, dict)
class Myclass(metaclass = MyMetaClass):
    pass

需求:类名必须首字母大写

class MyMetaClass(type):
    def __init__(self,what, bases=None, dict=None):
        if not what.istitle():
            raise Exception('首字母大写')
        super().__init__(what, bases, dict)
class Myclass(metaclass = MyMetaClass):
    pass

元类的进阶

元类不单单可以控制类的产生过程,其实也可以控制对象的

对象加括号执行产生该对象类里面的双下call方法

class Myclass:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __call__(self, *args, **kwargs):
        print("__call__")
obj = Myclass('nana',18)
obj()  # __call__

推导出:类加括号执行产生该类的元类里面的双下call方法

class Father_Father(type):
    def __call__(self, *args, **kwargs):
        print('__call__')
        print(args)
        print(kwargs)
        super().__call__(*args, **kwargs)

class Myclass(metaclass= Father_Father):
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print('__init__')
obj = Myclass('nana',18)  # __call__ ('nana', 18) {} __init__

总结:如果我们想高度定制对象的产生过程,可以操作元类里面的双下call方法,如果我们想要高度定制类的产生过程,可以操作元类里面的双下init方法

练习

需求:实例化对象 所有的参数都必须采用关键字参数的形式

class Father_Father(type):
    def __call__(self, *args, **kwargs):
        if args:
            raise Exception('不能有位置参数')
        super().__call__(*args, **kwargs)

class Myclass(metaclass= Father_Father):
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print('__init__')
obj = Myclass(name = 'nana',age = 18)   # __init__
print(obj.name)

双下new方法

1.类产生的步骤
	1.1 产生一个空对象  >>>self.__new__(*args.**kwargs)
    1.2 自动触发__init__方法实例化对象  >>> super().__call__(*args, **kwargs)
    1.3 返回实例化好的对象  >>>给对象添加属性
2.__new__方法专门用于产生空对象
  __init__方法专门用于给对象添加属性

作业

自定义字典并且让你字典具备d.key = value 修改键值对;d.key = value 添加键值对

class MyDict(dict):

    def __setattr__(self, key, value):
        super(MyDict, self).__setattr__(key, value)
        self[key] =  value # d['name']='nana'

d = MyDict({'name': 'cx', 'age': 18})
d.name = 'nana'  
print('d.name:', d.name)  # d.name: nana
print('d:',d)  # d: {'name': 'nana', 'age': 18}
d.age2=22
print(d)  # {'name': 'nana', 'age': 18, 'age2': 22}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值