博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
面向对象知识点
阅读量:6001 次
发布时间:2019-06-20

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

 

@(python)[笔记]

目录

一、isinstance()和issubclass()    1. isinstance()    2. issubclass()二、反射    1. hasattr()    2. getattr() 3. setattr() 4. delattr() 5. 扩展用法 三、__setattr__、__delattr__、__getattr__ 四、二次加工标准类型(包装) 授权 五、__next__和__iter__实现迭代器协议 六、__doc__ 七、__module__和__class__ 八、__str__ 九、__del__析构方法 十、__setitem__,__getitem__,__delitem__ 十一、__enter__和__exit__ 十二、__call__ 十三、元类metaclass 先来看看exec 未完

一、isinstance()和issubclass()

1. isinstance()

语法:isinstance(obj,cls)

功能:检查对象obj是否是类cls的实例

class foo: pass obj = foo() print(isinstance(obj,foo)) #True

2. issubclass()

语法:issubclass(sub, super)

功能:检查sub类是否是super类的派生类(子类)

class foo: pass obj = foo() print(isinstance(obj,foo)) #True class bar(foo): pass print(issubclass(bar,foo)) #True

二、反射

反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,也可以称为自省

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

python中可以实现自省的四个函数:

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

1. hasattr()

语法:hasattr(object,"name")

功能:判断object中有没有一个name字符串对应的方法或属性。
用法:

class foo: name = "egon" pass obj = foo() print(hasattr(obj,"name")) #True print(hasattr(obj,"func")) #True print(hasattr(foo,"name")) #True print(hasattr(foo,"func")) #True

2. getattr()

语法:getattr(object, "name"[, default=None])

功能:从对象object获取一个name字符串对应的方法,如果没有name字符串对应的方法,并且没有设置default,则报错;如果没有name字符串对应的方法,设置了default,则将default的值返回。getattr(object,"name")等价于object.name

class foo: name = "egon" def func(self): print("Hello world") obj = foo() print(getattr(obj,"func")) #
> #可以看出,输出结果是一个绑定方法 f1 = getattr(obj,"func") f2 = getattr(obj,"bar","不存在") f1() #Hello world print(f2) #不存在

3. setattr()

语法:setattr(x, y, v)

功能:设置属性,setattr(x,'y',v)等价于“x.y = v
用法:

class foo: name = "egon" def func(self): print("Hello world") obj = foo() setattr(obj,"age",20) setattr(obj,"show_name",lambda self:self.name+"_NB") print(obj.__dict__) #查看属性字典 print(obj.show_name(obj)) ''' 输出: {'age': 20, 'show_name': 
at 0x000000000115E2F0>} egon_NB '''

4. delattr()

语法:delattr(x, y)

功能:删除属性,delattr(x,'y')相当于``del x.y''
用法:

delattr(obj,"show_name")# delattr(obj,"sex") #不存在,则报错 print(obj.__dict__) #{'age': 20}

5. 扩展用法

反射当前模块

import sysdef s1(): print("s1") def s2(): print("s2") this_module = sys.modules[__name__] print(hasattr(this_module,"s1")) #True print(getattr(this_module,"s2")) #
,加括号可执行

导入其他模块,利用反射查找该模块是否存在某个方法

程序目录:

  • module_test.py
  • current.py
# module_test.pydef test(): print("from the module_test.test")
#current.pyimport module_test as mt print(hasattr(mt,"test")) #True f = getattr(mt,"test","不存在") f() #from the module_test.test

三、__setattr____delattr____getattr__

__setattr__ :添加/修改属性会触发它的执行;

__delattr__ :删除属性的时候会触发;
__getattr__ :只有在使用点调用属性且属性不存在的时候才会触发。

class Foo: x = 1 def __init__(self,y): self.y = y def __getattr__(self, item): print("---> 你找的属性不存在") def __setattr__(self, key, value): print("---> from __setattr__") # self.key = value #这样会陷入无限递归,只能通过__dict__字典进行赋值 self.__dict__[key] = value #这样才可以正确赋值 def __delattr__(self, item): print("---> from __delattr__") f1 = Foo(10) #相当于设值,触发__setattr__执行,---> from __setattr__ print(f1.__dict__) #{},直接打印为空,是因为你自己重写了__setattr__方法, # 而你在__setattr__方法中没有真正赋值 f1.z #触发__getattr__执行,---> 你找的属性不存在 del f1.x #触发__delattr__执行,---> from __delattr__

四、二次加工标准类型(包装)

包装:python为用户提供了标准数据类型,以及丰富的内置方法,其在很多场景下,我们需要基于标准数据类型来定制我们自己的数据类型,新增 / 改写方法,这就用到继承和派生的知识,其他标准类型均可以通过下面的方式进行二次加工。

示例1:对list进行二次加工,限制append只能增加int整型数据;并且增加mid方法,得到列表的中间值;其余方法都继承list的。

class List(list): def append(self,p_object): #派生出自己的append方法,会覆盖父类list中的append方法 if not isinstance(p_object,int): raise TypeError("%s must be int"%p_object) super(List, self).append(p_object) @property #中间值听起来更像一个属性,而非方法,所以使用property def mid(self): mid_num = len(self) // 2 return self[mid_num] l = List([1,2,3,4]) print(l) #[1, 2, 3, 4] l.append(5) print(l) #[1, 2, 3, 4, 5] print(l.mid) #中间值3

示例二:为listclear方法增加权限

class List(list): def __init__(self,item,perm=False): super(List, self).__init__(item) self.perm = perm #先设定一个默认的权限 def clear(self): if not self.perm: raise PermissionError("权限拒绝") super(List, self).clear() l = List([1,2,3]) print(l) #[1, 2, 3] # l.clear() #抛出“权限拒绝”的异常 l = List([1,2,3],True) #给一个授权参数为True l.clear() print(l) #[],可以正常清空列表

授权

授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建、修改或删除原有产品的功能,其它的则保持原样。授权的过程就是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

示例三:利用open()函数重新定制一个文件处理器,增加写内容添加时间的功能;

import timeclass FileHandler: def __init__(self,filename,mode='r',encoding="utf-8"): self.file = open(filename,mode,encoding=encoding) #self.file获取到一个文件句柄 def write(self,line): t = time.strftime("%Y-%m-%d %X") self.file.write("%s %s"%(t,line)) def __getattr__(self, item): return getattr(self.file,item) #当对象调用FileHandler类不存在的方法时,会返回open()函数的item字符串对应的方法; f1 = FileHandler("a.txt","r+") f1.write("你好吗\n") f1.seek(0) print(f1.tell()) #0

示例四:

#我们来加上b模式支持import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): if 'b' in mode: self.file=open(filename,mode) else: self.file=open(filename,mode,encoding=encoding) self.filename=filename self.mode=mode self.encoding=encoding def write(self,line): if 'b' in self.mode: if not isinstance(line,bytes): raise TypeError('must be bytes') self.file.write(line) def __getattr__(self, item): return getattr(self.file,item) def __str__(self): if 'b' in self.mode: res="<_io.BufferedReader name='%s'>" %self.filename else: res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding) return res f1=FileHandle('b.txt','wb') # f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气 f1.write('你好啊'.encode('utf-8')) print(f1) f1.close()

示例五:利用授权的方式重新定制listappend方法,只能往列表中添加int整型数据;与示例一作对比,看一下两者的区别。

#授权,定制listclass List: def __init__(self,seq): self.seq = list(seq) def append(self,p_object): if not isinstance(p_object,int): raise TypeError("'%s' must be int"%p_object) self.seq.append(p_object) def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l = List([1,2,3]) l.append("4") #TypeError: '4' must be int print(l)

总结:授权这种方式用在定制源不是类的情况下。例如示例三中,定制open()函数

五、__next____iter__实现迭代器协议

迭代器必须要有__next____iter__方法;

现在就来自己实现一个迭代器吧;
简单实现:

class Foo: def __init__(self,x): self.x=x def __iter__(self): return self def __next__(self): n=self.x self.x+=1 return self.x f=Foo(3) for i in f: print(i)

模拟实现range()函数:

class foo: def __init__(self,start,stop=None,step=1): self.start = start self.stop = stop self.step = step if isinstance(self.start,str) \ or isinstance(self.stop,str) \ or isinstance(self.step,str): raise InterruptedError("Must be Numeric") def __next__(self): if self.stop: res = self.compute() else: self.stop = self.start # 如果只传了一个数字,则将其设为迭代停止数字 self.start = 0 # 如果只传了一个数字,则默认从0开始迭代 res = self.compute() return res def __iter__(self): return self #迭代器执行__iter__方法,返回的是它本身 def compute(self): if self.start >= self.stop: #判断是否超出迭代停止数字 raise StopIteration # 这是超出迭代器范围后迭代器协议规定的抛出异常 iter_val = self.start #迭代后的值 self.start += self.step return iter_val for i in foo('a',20,3.5): print(i) for i in foo(5,15,3): print(i)

六、__doc__

查看对象的描述信息

def func(): '''我是函数的描述信息''' pass print(func.__doc__) class Foo: '''我是类的描述信息''' pass class bar(Foo): pass obj = Foo() print(obj.__doc__) print(Foo.__doc__) print(bar.__doc__) #该属性不会继承给子类 ''' 输出: 我是函数的描述信息 我是类的描述信息 我是类的描述信息 None '''

七、__module____class__

__module__ 表示当前操作的对象在那个模块

__class__ 表示当前操作的对象的类是什么

以下两个文件在同一级目录下:

#current.pyclass C: def __init__(self): self.name = "alex"
#test.pyimport current def test(): print("from the test.test") obj = current.C() print(obj.name) print(obj.__class__) #输出是哪个类实例化得到的对象 print(obj.__module__) #输出属性哪个模块 ''' 输出: alex 
current '''

八、__str__

l = list([1,2,3])print(l) #打印的是[1, 2, 3]
class mysql:    def __init__(self,host,port): self.host = host self.port = port conn = mysql("127.0.0.1","3306") print(conn) #打印的是<__main__.mysql object at 0x0000000000701898>

从以上两段代码可以看到,我们自己定义的类,生成的对象直接被打印时,打印的是对象的内存地址,而这并不是我们想要的,我们实际想要的也是像第一段代码那样返回一个有用的信息,这时就要用到__str__这个内置方法了,它定义在类的内部,只要类被实例化,就会自动触发__str__的执行,并返回一个值给实例化后的对象。如下示例:

class mysql: def __init__(self,host,port): self.host = host self.port = port def __str__(self): return "Host:%s,Port:%s"%(self.host,self.port) conn = mysql("127.0.0.1","3306") #会将`__str__`方法的返回值赋值给对象conn print(conn) ''' 输出: Host:127.0.0.1,Port:3306 '''

九、__del__析构方法

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

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

简单示例:

class Foo: def __del__(self): print("执行__del__") f1 = Foo() del f1 #释放f1,会触发__del__执行 print("------->") ''' 输出: 执行__del__ -------> '''

再看看下面这种情况:

class Foo: def __del__(self): print("执行__del__") f1 = Foo() # del f1 #注释掉 print("------->") ''' 输出: -------> 执行__del__ ''' #可以看出__del__仍然会在程序执行完毕后,被触发执行

十、__setitem__,__getitem__,__delitem__

类中有这三个方法,那就意味着类中的属性可以像字典一样进行操作;

class Foo: def __init__(self,name): self.name = name def __getitem__(self, item): print("from __getitem__") def __setitem__(self, key, value): print("from __setitem__") self.__dict__[key] = value #加入此代码,才会真正设值 def __delitem__(self, key): print("from __delitem__") self.__dict__.pop(key) f1 = Foo("egon") # f1.age = 18 #不会触发__setitem__的执行 f1["age_2"] = 20 #会触发__setitem__的执行,并不会真正设值; print(f1.__dict__) del f1["age_2"] print(f1.__dict__) print(f1["name"]) #会触发__getitem__执行,如果__getitem__没有设定返回值,则会返回一个None ''' 输出结果: from __setitem__ {'name': 'egon', 'age_2': 20} from __delitem__ {'name': 'egon'} from __getitem__ None ''' #从结果可以看出,age_2 = 20并没有真正设值成功

十一、__enter____exit__

通过__enter____exit__这两个方法可以实现上下文件管理协议,即with语句。为了让一个对象兼容with语句,必须在这个对象的类中声明__enter____exit__方法。

class Open: def __init__(self,name): self.name = name def __enter__(self): #出现with语句,对象的__enter__方法就会被触发,有返回值则赋值给as声明的变量 print("from __enter__") return 3 #会返回给as语句后面的变量 def __exit__(self, exc_type, exc_val, exc_tb): print("from __exit__, with代码块执行完毕时触发") with Open("egon") as f: print("from with 代码块") print("f:",f) ''' 输出: from __enter__ from with 代码块 f: 3 from __exit__, with代码块执行完毕时触发 '''

__exit__(self, exc_type, exc_val, exc_tb)中的三个参数的含义:

  • exc_type 代表异常类型
  • exc_val 代表异常值
  • exc_tb 代表异常的追溯信息
class Open: def __init__(self,name): self.name = name def __enter__(self): #出现with语句,对象的__enter__方法就会被触发,有返回值则赋值给as声明的变量 # print("from __enter__") return self #会返回给as语句后面的变量 def __exit__(self, exc_type, exc_val, exc_tb): print("from __exit__, with代码块执行完毕时触发") print("exc_type:",exc_type) print("exc_val:",exc_val) print("exc_tb:",exc_tb) return True #返回True,则表示不抛出异常,with代码之后的代码可正常执行,但with子代码raise后的代码不会执行。 with Open("egon") as f: print("from with 代码块") raise AttributeError("属性错误") # print("=====> 在异常之后") #不会执行 print("=====> 在异常之后,with之外") #__exit__返回True才会执行 ''' 输出: from with 代码块 from __exit__, with代码块执行完毕时触发 exc_type: 
exc_val: 属性错误 exc_tb:
=====> 在异常之后 '''

示例:模拟open(),并实现上下文管理

#模拟open()功能class Open: def __init__(self,filename,mode='r',encoding="utf-8"): self.filename = filename self.mode = mode self.encoding = encoding def __enter__(self): self.file = open(self.filename,self.mode,encoding =self.encoding) return self.file def __exit__(self, exc_type, exc_val, exc_tb): self.file.close() return True #遇到异常不抛出,with之外的代码继续执行 with Open("a.txt","w+") as f: f.write("11111\n") f.write("22222\n") f.seek(0) print(f.tell()) f.abc #抛出异常,交给__exit__处理

十二、__call__

对象后面加括号,就会触发__call__方法的执行;反之类中有了__call__方法,通过这个类生成的对象,才能加括号执行。

class foo: def __init__(self): print("from __init__") def __call__(self, *args, **kwargs): print("from __call__") obj = foo() #执行__init__ obj() #执行__call__ ''' 输出: from __init__ from __call__ '''

转载于:https://www.cnblogs.com/chenghao1994/p/7221935.html

你可能感兴趣的文章
Go语言标准库之JSON编解码
查看>>
winpcap 发送数据包
查看>>
cisco 出现 %Error opening tftp://255.255.255.255 错误解决办法
查看>>
VIM编辑器
查看>>
IE主页被篡改 地址框变灰
查看>>
linux上架设l2tp+ipsec ***服务器
查看>>
Facebook和用户界面会如何扭曲你说的话
查看>>
安卓混合开发之Cordova,NativeWebView两种实现
查看>>
git设置socks代理
查看>>
桶排序
查看>>
石化数字化交付
查看>>
如何用windows Live writer 撰写blog
查看>>
RHEL6入门系列之十九,硬盘分区与格式化
查看>>
Linux下升级 OpenSSH
查看>>
标准功能模块组件 -- 名片管理组件,C\S 版本的标准用例程序,可以参考权限实现方法...
查看>>
zygote进程图
查看>>
ldap快速配置
查看>>
docker之docker-machine用法
查看>>
IIS 7启用static JSON文件能POST方法
查看>>
P5205 【模板】多项式开根
查看>>