首页 > 06 面向对象之:反射,双下方法

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

一、反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

四个可以实现自省的函数

下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

class Foo:f = '类的静态变量'def __init__(self,name,age):self.name=nameself.age=agedef say_hi(self):print('hi,%s'%self.name)obj=Foo('egon',73)#检测是否含有某属性
print(hasattr(obj,'name'))
print(hasattr(obj,'say_hi'))#获取属性
n=getattr(obj,'name')
print(n)
func=getattr(obj,'say_hi')
func()print(getattr(obj,'aaaaaaaa','不存在啊')) #报错#设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)
print(obj.show_name(obj))#删除属性
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show_name111')#不存在,则报错print(obj.__dict__)对实例化对象的示例
对实例化对象的反射
class Foo(object):staticField = "old boy"def __init__(self):self.name = 'wupeiqi'def func(self):return 'func'@staticmethoddef bar():return 'bar'print getattr(Foo, 'staticField')
print getattr(Foo, 'func')
print getattr(Foo, 'bar')
对类的反射
import sysdef s1():print 's1'def s2():print 's2'this_module = sys.modules[__name__]hasattr(this_module, 's1')
getattr(this_module, 's2')
当前模块的反射
#一个模块中的代码
def test():print('from the test')
"""
程序目录:module_test.pyindex.py当前文件:index.py
"""
# 另一个模块中的代码
import module_test as obj#obj.test()print(hasattr(obj,'test'))getattr(obj,'test')()其他模块的示例
其他模块的反射

反射的应用:

了解了反射的四个函数。那么反射到底有什么用呢?它的应用场景是什么呢?

现在让我们打开浏览器,访问一个网站,你单击登录就跳转到登录界面,你单击注册就跳转到注册界面,等等,其实你单击的其实是一个个的链接,每一个链接都会有一个函数或者方法来处理。

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('欢迎来到存储页面')user = User()
while 1:choose = input('>>>').strip()if hasattr(user,choose):func = getattr(user,choose)func()else:print('输入错误。。。。')
学了反射之后的解决方式

有多简单,一目了然。

二. 函数 vs 方法

学到这里,我终于能回答你一直以来可能有的一个疑问。那就是,之前的学习中我们称len()为函数(口误时称为方法)却称如str的strip为方法,那它到底叫什么?函数和方法有什么区别和相同之处?我在这里就正式的解释一下。

2.1 通过打印函数(方法)名确定

def func():passprint(func)  # class A:def func(self):passprint(A.func)  # 
obj = A()
print(obj.func)  # >
View Code

2.2 通过types模块验证

from types import FunctionType
from types import MethodTypedef func():passclass A:def func(self):passobj = A()print(isinstance(func,FunctionType))  # True
print(isinstance(A.func,FunctionType))  # True
print(isinstance(obj.func,FunctionType))  # False
print(isinstance(obj.func,MethodType))  # True
View Code

2.3 静态方法是函数

from types import FunctionType
from types import MethodTypeclass A:def func(self):pass@classmethoddef func1(self):pass@staticmethoddef func2(self):pass
obj = A()# 静态方法其实是函数
# print(isinstance(A.func2,FunctionType))  # True
# print(isinstance(obj.func2,FunctionType))  # True
View Code

三. 双下方法

定义:双下方法是特殊方法,他是解释器提供的 由爽下划线加方法名加双下划线 __方法名__的具有特殊意义的方法,双下方法主要是python源码程序员使用的,我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。

调用:不同的双下方法有不同的触发方式,就好比盗墓时触发的机关一样,不知不觉就触发了双下方法,例如:__init__

3.01 __len__

class B:def __len__(self):print(666)b = B()
len(b) # len 一个对象就会触发 __len__方法。class A:def __init__(self):self.a = 1self.b = 2def __len__(self):return len(self.__dict__)
a = A()
print(len(a))
View Code

3.02 __hash__

class A:def __init__(self):self.a = 1self.b = 2def __hash__(self):return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))
View Code

3.03 __str__

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

class A:def __init__(self):passdef __str__(self):return '太白'
a = A()
print(a)
print('%s' % a)
View Code

3.04 __repr__

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

class A:def __init__(self):passdef __repr__(self):return '太白'
a = A()
print(repr(a))
print('%r'%a)
View Code

3.05 __call__

对象后面加括号,触发执行。

注:构造方法__new__的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):print('__call__')obj = Foo() # 执行 __init__
obj()       # 执行 __call__
View Code

3.06 __eq__

class A:def __init__(self):self.a = 1self.b = 2def __eq__(self,obj):if  self.a == obj.a and self.b == obj.b:return True
a = A()
b = A()
print(a == b)
View Code

3.07 __del__

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

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

3.08__new__

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

3.09 __item__系列

class Foo:def __init__(self,name):self.name=namedef __getitem__(self, item):print(self.__dict__[item])def __setitem__(self, key, value):self.__dict__[key]=valuedef __delitem__(self, key):print('del obj[key]时,我执行')self.__dict__.pop(key)def __delattr__(self, item):print('del obj.key时,我执行')self.__dict__.pop(item)f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
View Code

3.10 上下文管理器相关

__enter__  __exit__

# 如果想要对一个类的对象进行with  as 的操作 不行。
class A:def __init__(self, text):self.text = textwith A('大爷') as f1:print(f1.text)
没他们不可以这样操作
class A:def __init__(self, text):self.text = textdef __enter__(self):  # 开启上下文管理器对象时触发此方法self.text = self.text + '您来啦'return self  # 将实例化的对象返回f1def __exit__(self, exc_type, exc_val, exc_tb):  # 执行完上下文管理器对象f1时触发此方法self.text = self.text + '这就走啦'with A('大爷') as f1:print(f1.text)
print(f1.text)
有他们可以这样操作
class Diycontextor:def __init__(self,name,mode):self.name = nameself.mode = modedef __enter__(self):print "Hi enter here!!"self.filehander = open(self.name,self.mode)return self.filehanderdef __exit__(self,*para):print "Hi exit here"self.filehander.close()with Diycontextor('py_ana.py','r') as f:for i in f:print i
自定义文件管理器

 相关面试题:

class StarkConfig:def __init__(self,num):self.num = numdef run(self):self()def __call__(self, *args, **kwargs):print(self.num)class RoleConfig(StarkConfig):def __call__(self, *args, **kwargs):print(345)def __getitem__(self, item):return self.num[item]v1 = RoleConfig('alex')
v2 = StarkConfig('太白金星')
# print(v1[1])
# print(v2[2])
v1.run()-------
class UserInfo:passclass Department:passclass StarkConfig:def __init__(self, num):self.num = numdef changelist(self, request):print(self.num, request)def run(self):self.changelist(999)class RoleConfig(StarkConfig):def changelist(self, request):print(666, self.num)class AdminSite:def __init__(self):self._registry = {}def register(self, k, v):self._registry[k] = vsite = AdminSite()
site.register(UserInfo, StarkConfig)
# 1 
# obj = site._registry[UserInfo]()# 2
obj = site._registry[UserInfo](100)
obj.run()-------
class UserInfo:passclass Department:passclass StarkConfig:def __init__(self,num):self.num = numdef changelist(self,request):print(self.num,request)def run(self):self.changelist(999)class RoleConfig(StarkConfig):def changelist(self,request):print(666,self.num)class AdminSite:def __init__(self):self._registry = {}def register(self,k,v):self._registry[k] = v(k)site = AdminSite()
site.register(UserInfo,StarkConfig)
site.register(Department,RoleConfig)for k,row in site._registry.items():row.run()-------
class A:list_display = []def get_list(self):self.list_display.insert(0,33)return self.list_displays1 = A()
print(s1.get_list())-------
class A:list_display = [1, 2, 3]def __init__(self):self.list_display = []def get_list(self):self.list_display.insert(0, 33)return self.list_displays1 = A()
print(s1.get_list())------
class A:list_display = []def get_list(self):self.list_display.insert(0,33)return self.list_displayclass B(A):list_display = [11,22]s1 = A()
s2 = B()
print(s1.get_list())
print(s2.get_list())
View Code

 

转载于:https://www.cnblogs.com/Big-Dinosaur/p/10486909.html

更多相关:

  • class str(basestring):"""str(object='') -> stringReturn a nice string representation of the object.If the argument is a string, the return value is the same object."""d...

  • 目录结构: contents structure [-] 类的基本使用专有方法继承单重继承多重继承砖石继承 1.类的基本使用 下面是类使用的一个简单案例, class person:"person 类的描述信息"def __init__(self,name="",age=0):self.name = nameself.age =...

  • 一、set集合的方法 set不是特别常用,但是set的一些特性可以方便处理一些特殊情况。 集合(set)是一个无序不重复元素的序列。 可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。 创建格式: parame = {value01,va...

  • 1>UITextField实现leftView: self.inputTextField = [[UITextField alloc]initWithFrame:CGRectMake(10, 10, 200, 25)];self.inputTextField.delegate = self;self.inputTextField....

  • #coding:utf-8'''Created on 2017年10月25日@author: li.liu'''import pymysqldb=pymysql.connect('localhost','root','root','test',charset='utf8')m=db.cursor()'''try:#a=raw_inpu...

  • python数据类型:int、string、float、boolean 可变变量:list 不可变变量:string、元组tuple 1.list list就是列表、array、数组 列表根据下标(0123)取值,下标也叫索引、角标、编号 new_stus =['刘德华','刘嘉玲','孙俪','范冰冰'] 最前面一个元素下标是0,最...

  • from pathlib import Path srcPath = Path(‘../src/‘) [x for x in srcPath.iterdir() if srcPath.is_dir()] 列出指定目录及子目录下的所有文件 from pathlib import Path srcPath = Path(‘../tenso...

  • 我在使用OpenResty编写lua代码时,需要使用到lua的正则表达式,其中pattern是这样的, --热水器设置时间 local s = '12:33' local pattern = "(20|21|22|23|[01][0-9]):([0-5][0-9])" local matched = string.match(s, "...

  • 在分析ats的访问日志时,我经常会遇到将一些特殊字段对齐显示的需求,网上调研了一下,发现使用column -t就可以轻松搞定,比如 找到ATS的access.log中的200响应时间过长的日志 cat access.log | grep ' 200 ' | awk -F '"' '{print $3}' > taoyx.log co...

  • 栈可以分为 顺序栈: 数组实现链式栈: 链表实现空间复杂度 栈的空间复杂度: 有一个n个元素的栈, 在入栈和出栈过程中, 只需要存储一个临时变量存储空间, 所以空间复杂度是O(1) 并不是说栈有n个元素, 空间复杂度就是O(n), 而是指除了原本的空间外, 算法需要的额外空间 栈要满足后进先出(LIFO)的特性, 栈有以下几种方法...

  • 什么是闭包   python中函数名是一个特殊的变量,它可以作为另一个函数的返回值,而闭包就是一个函数返回另一个函数后,其内部的局部变量还被另一个函数引用。   闭包的作用就是让一个变量能够常驻内存。 def count():fs = []for i in range(1, 4):def f():return i*ifs.appen...

  • 最近尝试了一下Android的Gradle打包,发现确实比Ant打包会方便很多,特此记录下来。 注:android的gradle现在插件的版本已经是0.14.3了,对于一些老的方法和api,有一些已经被移除,无法使用(http://tools.android.com/tech-docs/new-build-system/migrati...