面向对象:反射和双下方法

Posted tutougold

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象:反射和双下方法相关的知识,希望对你有一定的参考价值。

面向对象:反射和双下方法

1.元类type

type:获取对象从属于的类
class A:
    pass
obj = A()
print(type(A))
print(type('abc'))
print(type([1,2,3]))

python中一切皆对象,类在某种意义上也是一个对象,python中自己定义的类,以及大部分内置类都是由type元类(构建类)实例化得来的

type 与 object 的关系
object类是type类的一个实例,object类是type的父类.
print(type(object))
print(issubclass(type,object))

2.反射

反射:程序对自己内部代码的一种自省方式.
反射是通过字符串去操作对象的方式.
四个维度:实例对象,类,本模块,其他模块
四个方法:
hasattr,getattr,setattr,delattr
    
1.实例对象:
class A:
    country = "中国"
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def func(self):
        print("in A func")
obj = A("奇奇",18)

#hasattr
print(hasattr(obj,'name'))  #True
print(hasattr(obj,'country')) #True
print(hasattr(obj,'func'))    #True

#getattr
print(getattr(obj,'name'))      #奇奇
print(getattr(obj,'country'))   #中国
f = getattr(obj,'func') 
f()                             # in A func

#setattr
setattr(obj,'sex','男')
print(obj.__dict__)   #'name':'奇奇','age': 18,'sex':'男'
delattr(obj,'name')
print(obj.__dict__)  #'age': 18, 'sex': '男'

2.类
class A:
    country = "中国"
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def func(self):
        print("in A func")
        
if hasattr(A,'country'):
    print(getattr(A,'country'))     #中国
if hasattr(A,'func'):
    obj = A('奇奇',18)
    getattr(obj,'func')()      # in A func
    getattr(A,'func')(obj)     # in A func

3.本模块
a = 666
def func1():
    print('in func1')
def func2():
    print('in func2')
def func3():
    print('in func3')
def func4():
    print('in func4')
import sys
print(getattr(sys.modules[__name__],'a'))    # 666
getattr(sys.modules[__name__],'func1')()     # in func
#执行四个函数
func_lst = [f'funci' for i in range(1,5)]
for func in func_lst:
    getattr(sys.modules[__name__],func)()
    
4.从其他模块

#名字为 aaa.py的其他模块内容如下
name = '金星'
def func():
    print('in aaa func')
class C:
    area = '北京'
    def __init__(self,name):
        self.name = name
    def func(self):
        print('in C func')

import aaa
print(getattr(aaa,'name'))    #金星
getattr(aaa,'func')()         # in C func

import aaa
1.找到aaa对象的C类,实例化一个对象
obj = getattr(aaa,'C')('11')
2.找到aaa对象的C类,通过对C类这个对象使用反射取到area
print(getattr(aaa.C,'area'))
3.找到aaa对象的C类,实例化一个对象,对对象进行反射取值
obj = getattr(aaa,'C')('奇奇')
print(getattr(obj,'name'))

反射的应用:
    
没学反射之前:
class User:
    def login(self):
        print('欢迎来到登录页面')
    def register(self):
        print('欢迎来到注册页面')   
    def save(self):
        print('欢迎来到存储页面')
while 1:
    choose = input('>>>').strip()
    if choose == 'login':
        obj = User()
        obj.login()
    elif choose == 'register':
        obj = User()
        obj.register()
    elif choose == 'save':
        obj = User()
        obj.save()    
学了反射之后:
class User:
    def login(self):
        print('欢迎来到登录页面')
    def register(self):
        print('欢迎来到注册页面')   
    def save(self):
        print('欢迎来到存储页面')
while 1:
    choose = input('请输入序号: \n 1:登录 \n 2: 注册 \n 3:存储\n').strip()  
    obj = User()
    getattr(obj, obj.user_list[int(choose)-1][0])()  

3.函数与方法的区别

def func1():
    pass
class A:
    def func(self):
        pass
1.通过打印函数名区别是方法还是函数

print(func1)     #<function func1 at 0x0000024DEC521EA0>
print(A.func)    #<function A.func at 0x0000024DEC6FCA60>
                 #函数
obj = A()
print(obj.func) #<bound method A.func of <__main__.A object at 0x0000027A2DA8B6A0>>
                #方法
通过类名调用类中的实例方法叫做函数
通过对象调用类中的实例方法叫做方法

2.可以借助模块判断是方法还是函数

from types import FunctionType
from types import MethodType

print(isinstance(func,FunctionType))    #True
print(isinstance(A.func,FunctionType))  #True
print(isinstance(obj.func,FunctionType))   #False
print(isinstance(obj.func,MethodType))   #True

总结:
python中一切皆对象,类在某种意义上也是译者对象,python中自己定义的类以及大部分内置类,都是由type元类(构建类)实例化得来的
函数在某种意义上也是一个对象,是从FunctionType这个类实例化出来的
方法在某种意义上也是一个对象,是从MethodType这个类实例化出来的
函数都是显性传参,方法都是隐性传参
ps:在其他语言中,如Java中只有方法,C中只有函数,C++则取决于是否在类中。

4.双下方法

定义:双下方法是特殊方法,他是解释器提供的 由爽下划线加方法名加双下划线 __方法名__的具有特殊意义的方法,双下方法主要是python源码程序员使用的,我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。
调用:不同的双下方法有不同的触发方式,所以不能轻易使用
    
1. __len__
class B:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __len__(self):
        print(self.__dict__)
        return len(self.__dict__)
b = B("奇奇",18)
print(len(b))       # 'name': '奇奇', 'age': 18
                    # 2
    
2.__hash__
class B:
    def __init__(self):
        self.a = 1
        self.b = 2
    def __hash__(self):
        return hash(str(self.a)+str(self.b))
a = B()
print(hash(a))   #-6576024659466609941

3.如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。打印对象触发__str__方法.
class A:
    def __init__(self):
        pass
    def __str__(self):
        return '哈哈'
a = A()
print(a)     # 哈哈
print(str(a))   #哈哈    直接str转化也可以触发
 
4.如果一个类中定义了__repr__方法,那么在repr(对象) 时,默认输出该方法的返回值。
class A:
    def __init__(self):
        pass
    def __repr__(self):
        return '哈哈'
a = A()
print(a)       #哈哈
print(repr(a)) #哈哈    

5.__call__  对象后面加括号,自动触发从属于类(父类)的__call__方法。
class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__')
obj = Foo() 
obj()       # __call__

6.__eq__
class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __eq__(self,obj):
        if  self.a == obj.a and self.b == obj.b:
            return True
a = A()
b = A()
print(a == b) #True

7.__del__  析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class A:

    def __del__(self):
        print(666)

obj = A()    #666
del obj

8.__new__ 构造方法,使用类名()创建对象时,python解释器首先会调用__new__方法为对象分配空间.
class A:
    def __init__(self):
        self.x = 1
        print('in init function')
    def __new__(cls, *args, **kwargs):
        print('in new function')
        return object.__new__(A, *args, **kwargs)

a = A()
print(a.x)
#in new function
#in init function
#1

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

以上是关于面向对象:反射和双下方法的主要内容,如果未能解决你的问题,请参考以下文章

面向对象的反射和双下方法(魔术方法)

Python面向对象反射,双下方法

Python面向对象之反射,双下方法

python 面向对象专题:元类type反射函数与类的区别特殊的双下方法

python 面向对象专题:元类type反射函数与类的区别特殊的双下方法

面向对象之 元类 , 反射 , 双下方法