反射 内置方法 元类

1 反射

1.1 什么是反射机制

Python属于动态语言,即程序执行变量定义语句时才确定变量的类型。

反射机制指的是在程序的运行过程中能够动态地获取对象信息以及动态地调用对象功能的一种解决方案。

在程序的运行过程中,可以获取并调用这个对象的所有属性和方法。

1.2 为什么使用反射

当需要访问某个对象的属性,却不能确定这个对象是否拥有这个属性时,需要做判断。
反射可以理解为一种能力,在程序运行过程中能够临时分析出对象/类中具有的属性/方法。

def check_attr(obj, attr):if attr not in obj.__dict__:return Falseelse:return Trueclass A:passa_obj = A()
a_obj.num = 10
if check_attr(a_obj, 'num'):print(a_obj.num)

但是上述方法存在局限性,对象调用__dict__只能获取对象自己(self)的属性和方法。

1.3 如何使用反射

1.3.1 dir和__dict__
class A:passa_obj = A()
a_obj.num = 10num_str = dir(a_obj)[-1]
print(num_str, type(num_str))  # num <class 'str'>
print(a_obj.__dict__[num_str])  # 10

dir(obj) 返回由参数obj的属性名和方法名组成的列表,但其元素都是字符串类型,不方便直接调用。
另外,不建议通过__dict__直接操作对象。

1.3.2 四个内置函数

hasattr,getattr,setattr,delattr
这四个内置函数通过字符串操作对象的属性/方法,即通过字符串反射到对象的属性/方法上,Python通过这四个内置函数实现反射机制。

  1. hasattr
    hasattr(obj, attr_name)
    用于判断对象是否包含对应的属性。
  2. getattr
    getattr(obj, attr_name[, default])
    用于返回一个对象属性值。
    其中default — 默认返回值,如果不提供该参数,在没有对应属性时会抛出异常AttributeError。
  3. setattr
    setattr(obj, attr_name, attr_value)
    用于设置属性值,该属性不一定是存在的。
  4. delattr
    delattr(obj, attr_name)
    用于删除属性。
class A:name = 'A'a_obj = A()print(a_obj.name)  # Aif hasattr(a_obj, 'name'):setattr(a_obj, 'name', 'A_Name')
else:passprint(a_obj.name)  # A_Name
class Ftp:def put(self):print('执行上传操作。')def get(self):print('执行下载操作')def interactive(self):method_input = input('>>>').strip()method = getattr(self, method_input, None)if method is None:print('指令不存在。')else:method()ftp_obj = Ftp()
ftp_obj.interactive()

2 内置方法

2.1 介绍

定义在类的内部,以双下划线__开头并以双下划线__结尾的方法。

内置方法会在满足一定条件时自动触发执行,无需手动调用。

使用内置方法可以定制类或对象。

len('abc')  # 会自动触发 'abc'.__len__()print('abc')  # 会自动触发 'abc'.__str__()

2.2 常用内置方法

  1. 内置方法__str__
    打印对象本身时自动触发,
    返回值必须是字符串类型。
class Person:def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):# 这里会自动触发对象自己的方法__str__(),导致递归调用。# print(self)  return f'姓名:{self.name},年龄:{self.age}'p1 = Person('刘翠英', 25)  # 姓名:刘翠英,年龄:25
  1. 内置方法__del__
    删除(清理)对象时自动触发。
class Person:def __init__(self, name, age):self.name = nameself.age = agedef __del__(self):print(f'删除对象{self.name}')p1 = Person('刘翠英', 25)
# del p1

即使不执行语句 del p1,当程序结束运行时会清理对象,清理对象时会自动触发对象的方法__del__。
内置方法__del__一般用于发起系统调用,回收系统资源,例如对象中的一个属性对应了一个打开的文件,或者网络连接等。

3 元类

如果希望为函数添加功能,而且不修改函数的代码和调用方式,使用装饰器;
如果希望修改类的功能,而且不修改类的代码和调用方式,使用元类。

3.1 介绍

Python中一切皆为对象。
如何得到一个对象?通过类的实例化,即调用类后返回一个对象。

类本身也是对象,类对象是通过调用元类实例化得到的。

元类 —(实例化)—> 类 —(实例化)—> 对象

定义元类可以用于控制类的产生以及类的调用。

  1. 控制类的产生
    在元类中定义方法__new__和__init__;
  2. 控制类的调用
    在元类中定义方法__call__。
class Demo:pass# 自定义类
print(type(Demo))  # <class 'type'># 内置类
print(type(int))  # <class 'type'>print(type(type))  # <class 'type'>

3.2 关键字class产生类的流程

3.2.1 分析1 类的组成

一个类由三部分组成:类名,基类,类体

# 类名
class_name = 'People'  # 基类
class_bases = (object,)  # 类体 字符串
class_body = """
def __init__(self, name, age):self.name = nameself.age = age
"""# 类的名称空间
class_dict = {}
3.2.2 分析2 执行类体代码

函数exec

  1. exec(object[, globals[, locals]])
  2. object:待执行的Python代码,必须是字符串或code对象。
  3. globals:表示全局命名空间,用于存放全局变量,必须是一个字典对象。
  4. locals:表示当前局部命名空间,用于存放局部变量,可以是任何映射对象。如果该参数被忽略,那么它将会取与globals相同的值。
  5. 函数exec的返回值永远是None。

执行类体代码产生类的名称空间,并将执行过程中产生的名字放入类的名称空间中。

exec(class_body, {}, class_dict)print(class_dict)  # {'__init__': <function __init__ at 0x地址>}
3.2.3 分析3 调用元类产生类
# 调用元类产生类
NewPeopleClass = type(class_name, class_bases, class_dict)

函数type的参数:类名,类的基类,类的名称空间。

NewPeopleClass只是个变量名。

print(NewPeopleClass)  # <class '__main__.People'>
print(NewPeopleClass.__name__)  # People
3.2.4 分析4 调用类产生对象

使用自定义类NewPeopleClass实例化产生对象。

people_obj = NewPeopleClass('刘翠英', 25)
3.2.5 总结

关键字class产生类的步骤
步骤1:获取类名;
步骤2:获取类的基类;
步骤3:执行类体代码,并将执行过程中产生的名字存入类的名称空间中;
步骤4:调用元类实例化产生类。

3.3 元类

元类是一种特殊的类,用于实例化产生类。
通过自定义元类可以对类的产生过程进行控制,即可以动态地创建类。

3.3.1 type动态地创建类

type可以用于动态地创建类,参数是类的描述信息。

type(类名,由父类名称组成的元组,包含属性/方法的字典)

def print_obj_num(self):print(self.num)@classmethod
def print_cls_num(cls):print(cls.num)@staticmethod
def print_none():passDemo = type("Demo",(),{'num': 10,"print_obj_num": print_obj_num,"print_cls_num": print_cls_num,"print_none": print_none}
)print(Demo.__base__.__name__)  # object

等价于

class Demo:num = 10def print_obj_num(self):print(self.num)@classmethoddef print_cls_num(cls):print(cls.num)@staticmethoddef print_none():pass
3.3.2 指定元类产生类
class 类名(父类1, 父类2..., metaclass=type):pass

指定参数metaclass后,可以按照指定的方式创建新类,即调用指定的函数/元类后返回的对象为新产生的类。
指定函数,新产生的类是函数的返回值;
指定元类,新产生的类是元类中的方法__new__的返回值。

如果没有指定参数metaclass,默认使用type创建新类。

3.3.3 自定义元类

继承元类type的类是自定义元类。

class CustomMeta(type):pass
3.3.4 通过自定义元类产生类的过程

通过自定义元类产生类的过程

  1. 产生一个空对象,自动调用方法__new__;
  2. 自动调用类元类CustomMeta中的方法__init__,将新类(self)以及其类名,类的基类和类的名称空间传入方法__init__中,为新对象(新类)完成初始化操作;
  3. 返回完成初始化的对象,即新的类。
class CustomMeta(type):def __init__(self, class_name, class_bases, class_dict):super().__init__(class_name, class_bases, class_dict)print(self)  # <class '__main__.People'>print(class_name)  # Peopleprint(class_bases)  # ()print(class_dict)  # {'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x地址>}class People(metaclass=CustomMeta):def __init__(self):pass

通过元类type产生的类如果没有指定父类,会自动继承父类object。

class CustomMeta(type):def __init__(self, class_name, class_bases, class_dict):super().__init__(class_name, class_bases, class_dict)print(class_bases)  # ()class People(metaclass=CustomMeta):def __init__(self):passprint(People.__base__.__name__)  # object
3.3.5 示例
  1. 自定义的类的类名首字母必须大写。
class CustomMeta(type):def __init__(self, class_name, class_bases, class_dict):if class_name[0].islower():raise NameError('类名首字母必须大写。')super().__init__(class_name, class_bases, class_dict)class People(object, metaclass=CustomMeta):passclass people(object, metaclass=CustomMeta):  # NameError: 类名首字母必须大写。pass
  1. 类中必须有文档注释
class CustomMeta(type):def __init__(self, class_name, class_bases, class_dict):if '__doc__' not in class_dict or len(class_dict['__doc__'].strip()) == 0:raise TypeError('类中必须有文档注释。')super().__init__(class_name, class_bases, class_dict)class People(object, metaclass=CustomMeta):"""People"""passclass People1(object, metaclass=CustomMeta):  # TypeError: 类中必须有文档注释。""""""pass

3.4 方法__new__

产生一个空对象时会自动调用方法__new__。
调用方法__new__产生空对象,再调用方法__init__对空对象进行初始化。

class CustomMeta(type):# __init__的参数self是由__new__造的空对象# __init__的后三个参数来自__new__的参数*args和**kwargsdef __init__(self, class_name, class_bases, class_dict):super().__init__(class_name, class_bases, class_dict)def __new__(cls, *args, **kwargs):print(cls)# <class '__main__.CustomMeta'> 即当前所在的类print(*args)# People (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'People', '__doc__': 'People'}# 类名 基类组成的元组 类的名称空间组成的字典print(super().__name__)  # <attribute '__name__' of 'type' objects>return super().__new__(cls, *args, **kwargs)class People(object, metaclass=CustomMeta):pass

3.4 方法__call__

如果一个对象需要是可调用的,则可以在对象的类中定义方法__call__。
调用对象就会自动触发其类的方法__call__。
类的方法__call__一般会依次调用其对象的方法__new__和__init__。

class A:def __call__(self, *args, **kwargs):print('call')return 123a = A()
res = a()  # call
print(res)  # 123

对象实例化需要调用类,类能够被调用说明其元类中定义了方法__call__,
元类中的方法__call__做了三件事完成了对象的实例化过程:

  1. 调用类中的方法__new__产生一个空对象;
  2. 调用类中的方法__init__,为新对象完成初始化操作;
  3. 返回完成初始化的对象。

要点:

  1. 自定义元类可以被调用说明其元类type中定义了方法__call__。
  2. 自定义元类中的方法__call__控制了类的调用过程,即类的对象的产生过程。
class CustomMeta(type):def __init__(self, class_name, class_bases, class_dict):passdef __new__(cls, *args, **kwargs):return super().__new__(cls, *args, **kwargs)def __call__(self, *args, **kwargs):print(self)  # <class '__main__.People'># self在CustomMeta元类中,因此是元类的对象。# 此时元类的对象是新类Peopleprint(args)  # ('喜大壮', '26')print(**kwargs)# 调用其对象People中的方法__new__和方法__init__。new_obj = self.__new__(self)print(new_obj.__dict__)  # {}self.__init__(new_obj, *args, **kwargs)print(new_obj.__dict__)  # {'name': '喜大壮', 'age': '26'}return new_objclass People(object, metaclass=CustomMeta):def __init__(self, name, age):self.name = nameself.age = age# People中如果没有定义方法__new__,则会去其父类中寻找,最后会找到object.__new__(cls)def __new__(cls, *args, **kwargs):# return object.__new__(cls)return super().__new__(cls)p_obj = People('喜大壮', '26')

3.5 属性查找

原则:对象 => 类 => 父类
注意:父类不是元类
因此从对象开始查找时,不会找到元类中。

Python中一切皆为对象,包括类。
如果将类视为对象,产生类对象的类是元类。
因此从类开始查找时,
先作为类去访问父类,直到object,
然后作为对象去访问元类,直到type。

4 练习

4.1

通过元类将自定义类的非隐藏属性变成大写字母

class CustomMeta(type):def __new__(cls, class_name, class_bases, class_dict):new_class_dict = {}for each_key, each_value in class_dict.items():if not callable(each_value) and not each_key.startswith('__'):new_class_dict[each_key.upper()] = each_valueelse:new_class_dict[each_key] = each_valuereturn super().__new__(cls, class_name, class_bases, new_class_dict)class Demo(metaclass=CustomMeta):attr1 = Nonedef func(self):passprint(Demo.__dict__)

另一种方式

def upper_attr(class_name, class_bases, class_attr):new_attr = {}for each_name, each_value in class_attr.items():if not each_name.startswith('__'):new_attr[each_name.upper()] = each_valueelse:new_attr[each_name] = each_valuereturn type(class_name, class_bases, new_attr)class Demo(metaclass=upper_attr):attr1 = Nonedef func(self):passprint(Demo.__dict__)

4.2

类实例化时只能以关键字的形式传递参数,且参数将作为对象的属性,属性名均为大写字母。

class CustomMeta(type):def __call__(self, *args, **kwargs):if args:raise TypeError('Only keyword arguments can used.')obj = self.__new__(self)for each_key, each_value in kwargs.items():obj.__dict__[each_key.upper()] = each_valuereturn objclass Demo(metaclass=CustomMeta):passd = Demo(attr1=123)
print(d.__dict__)

4.3

将类实例化产生对象时初始化的属性全部隐藏。

class CustomMeta(type):def __call__(self, *args, **kwargs):obj = self.__new__(self)self.__init__(obj, *args, **kwargs)obj.__dict__ = {f'_{self.__name__}__{each_key}': each_value for each_key, each_value in obj.__dict__.items()}return objclass Demo(metaclass=CustomMeta):def __init__(self, attr1):self.attr1 = attr1d = Demo(123)
print(d.__dict__)

29 反射 内置方法 元类相关推荐

  1. python_day21面向对象的进阶(反射,内置方法,)

    # 两个内置函数 *# 反射 *****# 内置方法 *** # 类(定义) # 静态属性 类属性(变量) 直接写在类中,全大写 # 动态属性 方法(函数) self # 类方法 @classmeth ...

  2. python 类的内置方法_Python 类的常用内置方法

    类的内置方法(魔法方法): 凡是在类内部定义,以__开头__结尾的方法,都是类的内置方法,类的内置方法,会在满足某种条件下自动触发. 1.1__new__ __new__:在___init__触发前, ...

  3. issubclass和isinstance 反射 内置方法(魔术方法)

    目录 issubclass 和 isinstance issubclass isinstance 反射 通过用户输入的key,value往对象中赋值 动态的往对象中放方法 动态的删除属性 动态删除对象 ...

  4. item系列内置方法重构类

    item系列 和对象使用[]访问值有联系 obj = {'k':'v'} print(obj) # 字典的对象 print(obj['k']) 在有些内置的模块中,有一些特殊的方法,要求对象必须实现_ ...

  5. 面向对象常用魔法方法(内置方法)合集, 超级无敌宇宙详细

    引入 众所周知,方法是需要调用执行的,而魔法方法则不一样,他无需你的调用,在特定的时候会自己执行, 例如我们之前所学的__init__, 在示例对象 ([类名]+()) 的时候触发执行它 1.什么是内 ...

  6. python内置类属性_Python内置方法和属性应用:反射和单例(推荐)

    1. 前言 python除了丰富的第三方库外,本身也提供了一些内在的方法和底层的一些属性,大家比较常用的如dict.list.set.min.max.range.sorted等.笔者最近在做项目框架时 ...

  7. python类的内置方法_python面向对象之类中的内置方法

    __setattr__,__delattr__,__getattr__,__getattribute__以及标准类型的二次加工 __setattr__,__delattr__,__getattr__的 ...

  8. python全栈开发基础【第十七篇】面向对象反射和内置方法

    一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被 ...

  9. python反射和高阶内置方法

    1.isinstance:判断对象和类的关系 #判断结果返回bool类型 class A:pass class B(A):pass a = A() print(isinstance(a,A)) #Tr ...

最新文章

  1. Access外键 级联更新、删除
  2. java存款程序_JAVA实现账户取款和存款操作
  3. PDF 补丁丁 0.6.0.3282 版发布(修复内存漏洞)
  4. 关于ArcGIS JS API中的map高度为400px的问题解决
  5. React 和 Vue的特点
  6. springMVC——SpringMVC原理详细解析
  7. python手动安装包_python pip如何手动安装二进制包
  8. 大佬 | 从啥也不会,到Java大佬,他就因为会了这一门绝技
  9. BC之旅(13) — 农庄的残冬
  10. 如何啃透周志华的《机器学习》西瓜书?
  11. Hadoop 之 Distcp官网介绍和注意事项
  12. jQuery Mobile中按钮<a>或<button>添加class样式ui-btn-*
  13. 我决定把IDEA神器这些你可能不知道的但是又实用的小技巧分享出来,超赞!
  14. 【亲测有效】无法定位链接器!请检查 tools\link.ini 中的配置是否正确的解决方案...
  15. 数据安全管理条例明确个人信息保护 360呼吁隐私保护重在企业
  16. 网站优化:测试网站速度的8款免费工具推荐
  17. lattice开发错误集合
  18. 谷歌浏览器设置默认,但是打不开外部超链接/点击超链接没反应
  19. 超级详细-NMOS、PMOS的工作原理及相关内容整理(下)
  20. Linux优秀软件整理 - 摘自Linux 开源中国

热门文章

  1. 情人节程序员用HTML网页表白【婚礼庆典-邀请函】 HTML5七夕情人节表白网页源码 HTML+CSS+JavaScript
  2. android 实现圆角按钮,Android实现圆角Button按钮
  3. 程序媛才能读懂的高级情话
  4. TensorFlow测试CPU、GPU
  5. 【数据结构】【离线操作】飘雪圣域
  6. 租用Topaz Video Enhance AI
  7. java获取该临时文件的Path File.createTempFile(script, .sh);
  8. 构建一个JPEG解码器(3):霍夫曼表
  9. 平台:VS2010+object2015(sdk)+zwCAD2015(开发版) 配置
  10. Android ndk编译vector,Android Studio 2.2.3找不到 vector_android-ndk_开发99编程知识库