博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
面向对象之反射与特殊成员
阅读量:6009 次
发布时间:2019-06-20

本文共 9263 字,大约阅读时间需要 30 分钟。

反射

反射是指程序可以访问、检测和修改它自身状态和行为的一种能力(自省)

python面向对象中的反射:通过字符串的形式操作对象相关的属性,为什么是字符串呢?因为很多时候接收到的数据是字符串,如果没有反射,我们很难通过该数据来获取对象的属性或者方法。

python中的反射是通过四个内置函数来实现的,只要能通过对象名.  的形式访问到的属性都可以通过反射来访问到。可以应用反射的对象有实例对象、类、其他模块和本模块

1 class Person(object): 2     country = "China" 3  4     def __init__(self, name, age): 5         self.name = name 6         self.age = age 7  8     def say_hi(self): 9         print("hi, %s" % self.name)10 11 12 p1 = Person("小鱼", 21)13 14 # 检测是否含有某个属性15 print(hasattr(p1, "name"))   # True16 print(hasattr(p1, "say_hi"))   # True17 18 # 获取属性19 print(getattr(p1, "age"))     # 2120 func = getattr(p1, "say_hi")21 func()    # hi, 小鱼22 23 # 获取不到时可以设置返回值24 g = getattr(p1, "gender")   # 报错25 g = getattr(p1, "gender", "不存在...")26 print(g)    # 不存在...27 28 # 设置属性29 setattr(p1, "gender", "女")30 setattr(p1, "show_name", lambda self: self.name+"姑娘")    # 设置方法31 print(p1.__dict__)    # {'name': '小鱼', 'age': 21, 'gender': '女', 'show_name': 
at 0x0000016E4F271E18>}32 print(p1.show_name(p1)) # 小鱼姑娘33 34 35 # # 删除属性36 delattr(p1, "age")37 delattr(p1, "show_name")38 delattr(p1, "show_name111") # AttributeError: show_name11139 40 print(p1.__dict__) # {'name': '小鱼'}
实例对象
1 class Company(object): 2     start_time = "2017-05-12" 3  4     def __init__(self): 5         self.name = "ABS" 6  7     def func(self): 8         return "func" 9 10     @staticmethod11     def bar():12         return "bar"13 14 15 print(getattr(Company, "start_time"))   # 2017-05-1216 print(getattr(Company, "func"))      # 
17 print(getattr(Company, "bar"))
# attr模块内容flag = Truedef func(a):    print(a)    return a+3class B:    name_list = ["Tom", "Robin", "Julia"]    def __init__(self, name, gender):        self.name = name        self.gender = gender    def func(self):        print(self)import attr# 1.找到func并执行# 2.调用B类并调用name_list  func函数# 3.找到B类实例化对象再找name, name_list func# 1.找到func并执行getattr(attr, "func")(7)   # 7# 2.调用B类并调用name_list  func函数ret = getattr(attr, "B")print(getattr(ret, "name_list"))    # ['Tom', 'Robin', 'Julia']getattr(ret, "func")(111)   # 111# 3.找到B类实例化对象再找name, name_list funcb1 = ret("小春", "男")print(getattr(b1, "name"))    # 小春print(getattr(b1, "name_list"))   # ['Tom', 'Robin', 'Julia']getattr(b1, "func")()   # 
其他模块
1 import sys 2  3  4 def func1(): 5     print(666) 6  7  8 def func2(): 9     print(888)10 11 12 obj = sys.modules[__name__]13 ret = input(">>>")14 getattr(obj, ret)()
本模块

练习

1 import sys 2 obj = sys.modules[__name__] 3  4  5 def func1(): 6     print("in func1") 7  8  9 def func2():10     print("in func1")11 12 13 def func3():14     print("in func1")15 16 17 func_list = ["func%s" % i for i in range(1, 4)]18 # print(func_list)19 20 for func in func_list:21     getattr(obj, func)()
练习

小结

1. 反射就是通过字符串来操作(访问,设置,删除)对象属性

2. 反射有四种方法:hasattr, getattr, setattr, delattr

3. 反射用法为:getattr(obj, "属性名"), obj为对象名,包括类,属性名包括静态属性名和方法名

 

类的特殊成员

__len__

我们知道str, list, dic等数据类型可以使用len方法,这是因为这几个类里有__len__方法,当我们使用len()时,会自动触发类里面的__len__方法,从而返回对象的长度。但是现在有一个问题,如果我们自己定义了一个类,我们想要计算实例对象的长度,该怎么做呢?我们可以自己定义一个__len__方法,然后我们调用len()时就可以自动触发__len__方法

1 class A: 2  3     def __init__(self, name, age, gender): 4         self.name = name 5         self.age = age 6         self.gender = gender 7  8     def func1(self): 9         pass10 11     def func2(self):12         pass13 14     def __len__(self):15         count = 016         for i in self.__dict__:17             count += 118         return count      # return len(self.__dict__)19 20 21 a = A("julia", 19, "女")22 # print(a.__dict__)23 print(len(a))    # 3

 

__hash__

1 class Computer: 2     date_of_manufacture = "2017-09-23" 3  4     def __init__(self, brand, type_name): 5         self.brand = brand 6         self.type_name = type_name 7  8  9 c1 = Computer("xiaomi", "pro")10 print(hash(c1))     # -9223371910015235040

我们定义的类里并没有__hash__方法,但是为什么也可以hash呢?这是因为Computer继承的object类里面有__hash__方法,我们可以通过查看源码验证这一点

 

1 class Computer: 2     date_of_manufacture = "2017-09-23" 3  4     def __init__(self, brand, type_name): 5         self.brand = brand 6         self.type_name = type_name 7  8     def __hash__(self): 9         return 66610 11 12 c1 = Computer("xiaomi", "pro")13 print(hash(c1))     # 666

 

__str__

如果一个类中定义了__str__方法,那么打印在打印对象时,默认输出该方法的返回值

class School:    def __init__(self, name, location):        self.name = name        self.location = location    def __str__(self):        return "华南施工大学"scut = School("华南理工大学", "五山")print(scut)    # 华南施工大学

 

__repr__

如果一个类中定义了__repr__方法,那么在repr(对象) 时,默认输出该方法的返回值。

1 class Language: 2     def __init__(self): 3         pass 4  5     def __repr__(self): 6         return "{1: 'a', 2: 'b'}" 7  8  9 l1 = Language()10 print(l1)    # {1: 'a', 2: 'b'}11 print(repr(l1))    # {1: 'a', 2: 'b'}

 

__call__

对象后面加括号,触发执行__call__,通俗点说,__call__就是把一个python对象变成可调用的

class Person:    def __init__(self, name, age, gender):        self.name = name        self.age = age        self.gender = gender    def __call__(self):        print("My name is %s" % self.name)        print("I'm %s years old" % self.age)obj = Person("julia", 9, "女")obj()  # My name is julia   I'm 9 years old

 

__eq__

 

1 class A: 2     def __init__(self): 3         self.a = 1 4         self.b = 2 5  6     def __eq__(self, other): 7         if self.a == other.a and self.b == other.b: 8             return True 9 10 11 obj1 = A()12 obj2 = A()13 # obj1 == obj2会自动触发__eq__方法,将==右边的对象传进去14 print(obj1 == obj2)     # True

 

__del__

析构方法,当对象在内存中释放时,自动触发执行

此方法无需定义,它的调用是由解释器在进行垃圾回收时自动触发执行的

 

__new__

实例化对象的三个步骤

# 1. 在内存中开辟一个对象空间(__new__开辟的—)# 2. 自动执行__init__方法,将空间传给self# 3. 在__init__中给对象封装属性

我们可以通过代码来验证此执行顺序 ,我们自己定义一个__new__方法,看看结果怎么样

1 class A: 2     def __init__(self, name): 3         self.name = name 4         print("in A __init__") 5  6     # 自己定义一个__new__方法 7     def __new__(cls, *args, **kwargs): 8         print(111) 9 10 11 a = A("xiaochun")12 print(a.name)   # AttributeError: 'NoneType' object has no attribute 'name'

为什么会报错呢,因为遇到A()时,解释器会去A的类里面找__new__方法,找到了就执行这个方法来开辟内存空间,但这个__new__方法是个假的方法,实际上开辟不了内存空间,因此最后返回给a的是一个None,因而也就会报错了

print(a)   # None

那么有没有办法自己模拟一个办法,有的

1 class A: 2     def __init__(self, name): 3         self.name = name 4         print("in A __init__") 5  6     def __new__(cls, *args, **kwargs): 7         print("in A __new__") 8         return object.__new__(cls)   # 注意这里一定要传入一个类名cls,表明这个实例是由哪个类创建的 9 10 11 a = A("xioachun")12 print(a.name)

执行结果

in A __new__in A __init__xioachun

in A __new__在in A __init__前面,可以说明__new__是在__init__前面执行的。

利用__new__可以是实现单例模式

单例模式

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。【采用单例模式动机、原因】对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。【单例模式优缺点】【优点】一、实例控制单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。二、灵活性因为类控制了实例化过程,所以类可以灵活更改实例化过程。【缺点】一、开销虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。二、可能的开发混淆使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。三、对象生存期不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用单例模式具体分析
单例模式由来及优缺点
1 class ThreebodyRole: 2     __instance = None 3  4     def __init__(self, name, gender, age): 5         self.name = name 6         self.gender = gender 7         self.age = age 8  9     def __new__(cls, *args, **kwargs):10         if cls.__instance is None:11             obj = object.__new__(cls)12             cls.__instance = obj13         return cls.__instance     # 不为空,就返回这个对象14 15 16 ret1 = ThreebodyRole("云天明", "男", 25)17 print(ret1.name)     # 云天明18 print(ret1)          # <__main__.A object at 0x0000023DA925C9B0>19 ret2 = ThreebodyRole("章北海", "男", 26)20 print(ret2.name)     # 章北海21 print(ret2)          # <__main__.A object at 0x0000023DA925C9B0>22 ret3 = ThreebodyRole("艾AA", "女", 22)23 print(ret3.name)     # 艾AA24 print(ret3)    # <__main__.A object at 0x0000023DA925C9B0>25 print(ret3.name)     # 艾AA 把之前对象的覆盖都给覆盖掉了
单例模式的实现

__item__

1 class Foo: 2     def __init__(self, name): 3         self.name = name 4  5     def __getitem__(self, item): 6         print(self.__dict__[item]) 7  8     def __setitem__(self, key, value): 9         self.__dict__[key] = value10 11     def __delitem__(self, key):12         print("del obj[key]时,我执行")13         self.__dict__.pop(key)14 15     def __delattr__(self, item):16         print("del obj.key时,我执行")17         self.__dict__.pop(item)18 19 20 f1 = Foo("程心")21 f1["age"] = 1822 print(f1.__dict__)    # {'name': '程心', 'age': 18}23 f1["age1"] = 1924 print(f1.__dict__)    # {'name': '程心', 'age': 18, 'age1': 19}25 26 del f1.age1           # del obj.key时,我执行27 # del f1["age"]28 print(f1.__dict__)    # {'name': '程心', 'age': 18}29 30 del f1["age"]         # del obj[key]时,我执行31 print(f1.__dict__)    # {'name': '程心'}32 33 f1["name"] = "大史"34 print(f1.__dict__)    # {'name': '大史'}
item相关

 

转载于:https://www.cnblogs.com/zzliu/p/10289183.html

你可能感兴趣的文章
使用 Windows PowerShell 连接到 Lync Online
查看>>
作业1 对这门课的希望和自己的目标
查看>>
jmeter性能测试小小的实战
查看>>
WCF、.Net Remoting、Web Service概念及区别
查看>>
强大的HTTP包装开源项目ASIHTTPRequest介绍
查看>>
.NET Core 2.0 Preview2 发布汇总
查看>>
根据IP查找计算机名
查看>>
C++_类继承6-继承和动态内存分配
查看>>
数据库优化经验总结
查看>>
Qt 使用 boost
查看>>
iOS define 宏定义 和 const定义常量区别
查看>>
salt-syndic使用
查看>>
VUE.JS 使用axios数据请求时数据绑定时 报错 TypeError: Cannot set property 'xxxx' of undefined 的解决办法...
查看>>
python基础知识
查看>>
python - os.path模块
查看>>
文件操作IO
查看>>
ML_Scaling to Huge Datasets & Online Learning
查看>>
zookeeper的安装与使用
查看>>
php基础知识【函数】(5)正则preg
查看>>
C# 接口的作用浅谈举例
查看>>