热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

一.反射反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研

一. 反射

  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

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

四个可以实现自省的函数

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

class Foo:
f = ‘类的静态变量‘
def __init__(self,name,age):
self.name=name
self.age=age

def 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‘

@staticmethod
def bar():
return ‘bar‘

print getattr(Foo, ‘staticField‘)
print getattr(Foo, ‘func‘)
print getattr(Foo, ‘bar‘)

对类的反射

import sys


def 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.py
index.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():
pass

print(func) #


class A:
def func(self):
pass

print(A.func) #
obj = A()
print(obj.func) # >


2.2 通过types模块验证

from types import FunctionType
from types import MethodType

def func():
pass


class A:
def func(self):
pass

obj = A()


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


2.3 静态方法是函数

from types import FunctionType
from types import MethodType

class A:

def func(self):
pass

@classmethod
def func1(self):
pass

@staticmethod
def func2(self):
pass
obj = A()

# 静态方法其实是函数
# print(isinstance(A.func2,FunctionType)) # True
# print(isinstance(obj.func2,FunctionType)) # True


2.4 函数与方法的区别

那么,函数和方法除了上述的不同之处,我们还总结了一下几点区别。

(1)函数的是显式传递数据的。如我们要指明为len()函数传递一些要处理数据。

(2)函数则跟对象无关。

(3)方法中的数据则是隐式传递的。

(4)方法可以操作类内部的数据。

(5)方法跟对象是关联的。如我们在用strip()方法是,是不是都是要通过str对象调用,比如我们有字符串s,然后s.strip()这样调用。是的,strip()方法属于str对象。

我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。

在其他语言中,如Java中只有方法,C中只有函数,C++么,则取决于是否在类中。


三. 双下方法

定义:双下方法是特殊方法,他是解释器提供的 由爽下划线加方法名加双下划线 __方法名__的具有特殊意义的方法,

双下方法主要是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 = 1
self.b = 2

def __len__(self):
return len(self.__dict__)
a = A()
print(len(a))


3.02 __hash__

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

def __hash__(self):
return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))


3.03 __str__

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

class A:
def __init__(self):
pass
def __str__(self):
return ‘太白‘
a = A()
print(a)
print(‘%s‘ % a)


3.04 __repr__

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

class A:
def __init__(self):
pass
def __repr__(self):
return ‘太白‘
a = A()
print(repr(a))
print(‘%r‘%a)


3.05 __call__

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

注:构造方法__new__的执行是由创建对象触发的,即:对象 = 类名() ;

而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

def __init__(self):
pass

def __call__(self, *args, **kwargs):

print(‘__call__‘)


obj = Foo() # 执行 __init__
obj() # 执行 __call__


3.06 __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)


3.07 __del__

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

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,

因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。


3.08__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)

class A:
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
obj = object.__new__(cls)
cls.__instance = obj
return cls.__instance

单例模式

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。

通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。

如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
【采用单例模式动机、原因】
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,

但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。

如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,

则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,

不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,

但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,

并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
【单例模式优缺点】
【优点】
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
【缺点】
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,

因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,

因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用

单例模式具体分析


3.09 __item__系列

class Foo:
def __init__(self,name):
self.name=name

def __getitem__(self, item):
print(self.__dict__[item])

def __setitem__(self, key, value):
self.__dict__[key]=value
def __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__)


3.10 上下文管理器相关

__enter__  __exit__

# 如果想要对一个类的对象进行with as 的操作 不行。
class A:
def __init__(self, text):
self.text = text

with A(‘大爷‘) as f1:
print(f1.text)

没他们不可以这样操作

class A:

def __init__(self, text):
self.text = text

def __enter__(self): # 开启上下文管理器对象时触发此方法
self.text = self.text + ‘您来啦‘
return self # 将实例化的对象返回f1

def __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 = name
self.mode = mode

def __enter__(self):
print "Hi enter here!!"
self.filehander = open(self.name,self.mode)
return self.filehander

def __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 = num

def 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:
pass


class Department:
pass


class StarkConfig:
def __init__(self, num):
self.num = num

def 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


site = AdminSite()
site.register(UserInfo, StarkConfig)
# 1
# obj = site._registry[UserInfo]()

# 2
obj = site._registry[UserInfo](100)
obj.run()

-------
class UserInfo:
pass

class Department:
pass

class StarkConfig:
def __init__(self,num):
self.num = num

def 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_display

s1 = 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_display


s1 = A()
print(s1.get_list())

------
class A:
list_display = []

def get_list(self):
self.list_display.insert(0,33)
return self.list_display

class B(A):
list_display = [11,22]


s1 = A()
s2 = B()
print(s1.get_list())
print(s2.get_list())

 


推荐阅读
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
author-avatar
Ajax
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有