面向对象之反射与特殊成员

反射

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

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"))   # True
16 print(hasattr(p1, "say_hi"))   # True
17 
18 # 获取属性
19 print(getattr(p1, "age"))     # 21
20 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': <function <lambda> 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_name111
39 
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     @staticmethod
11     def bar():
12         return "bar"
13 
14 
15 print(getattr(Company, "start_time"))   # 2017-05-12
16 print(getattr(Company, "func"))      # <function Foo.func at 0x000001EBDE1CAAE8>
17 print(getattr(Company, "bar")) 
# attr模块内容
flag = True


def func(a):
    print(a)
    return a+3


class 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 func
b1 = ret("小春", "")
print(getattr(b1, "name"))    # 小春
print(getattr(b1, "name_list"))   # ['Tom', 'Robin', 'Julia']
getattr(b1, "func")()   # <attr.B object at 0x000001280E63C390>
其他模块
 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         pass
10 
11     def func2(self):
12         pass
13 
14     def __len__(self):
15         count = 0
16         for i in self.__dict__:
17             count += 1
18         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 666
10 
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 = obj
13         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)     # 艾AA
24 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] = value
10 
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"] = 18
22 print(f1.__dict__)    # {'name': '程心', 'age': 18}
23 f1["age1"] = 19
24 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