Python之路【第六篇】python基础 之面向对象进阶

时间:2021-10-29 18:06:02

一 isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)检查是否obj是否是类 cls 的对象  和  issubclass(sub, super)检查sub类是否是 super 类的派生类

 class A:
pass class B(A):
pass print(issubclass(B,A)) # B是A的子类 ,返回True
a1 = A()
print(isinstance(a1,A)) # a1 是A 的实例

二 反射

1 什么是反射

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

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

四个可以实现自省的函数

 class People:
country = "Chain"
def __init__(self,name,age):
self.name = name
self.age = age
def play_basketball(self):
print("%s doing play basketball"%self.name)
def eat(self):
print("%s doing eat mice" %self.name)
p1 = People("xiaoming","") # #################### hasattr ##############
# print(hasattr(p1,"name")) # True 查看是否存在这个方法和属性
# print(hasattr(p1,"eat"))
# print(hasattr(p1,"eat1")) #False # #################### getattr ##############
# print(getattr(p1,"name")) # xiaoming 获取属性
# print(getattr(p1,"eat")) # <bound method People.eat of <__main__.People object at 0x0000027CAF6645C0>> 加括号执行
# func = getattr(p1,"play_basketball")
# func() # #################### setattr 设置属性##############
setattr(p1,"gender","nan") # 为实例添加属性
print(p1.__dict__)
setattr(p1,"show_name",lambda self:self.name)
print(p1.show_name(p1))
# #################### delattr 删除属性 ############## delattr(p1,"gender")
print(p1.__dict__)
delattr(p1,"show_name")
print(p1.__dict__)
# delattr(p1,"gender") # 不存在则报错

3 为什么用反射之反射的好处

好处一:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

 class FtpClient:
'ftp客户端,但是还么有实现具体的功能'
def __init__(self,addr):
print('正在连接服务器[%s]' %addr)
self.addr=addr
#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
func_get=getattr(f1,'get')
func_get()
else:
print('---->不存在此方法')
print('处理其他的逻辑') 不影响代码编写

好处二:动态导入模块(基于反射当前模块成员)

Python之路【第六篇】python基础 之面向对象进阶

 # ############ 导入第一种方法 ########
# from m1.t import *
# text1()
# # _text2() # 无法调用 # ############ 导入第二种方法 ########
# module_t = __import__("m1.t")
# print(module_t) # <module 'm1' (namespace)>
# module_t.t.text1() # ############ 导入第三种方法 ########
# import importlib
# m = importlib.import_module("m1.t")
# print(m) # <module 'm1.t' from 'C:\\Users\\18368\\PycharmProjects\\project\\week3\\day1-object-oriented\\m1\\t.py'>
# m.text1()
# m._text2()

三种导入模式

反射总结:

hasattr(obj,'属性') #obj.属性 是否存在
getattr(obj,'属性') #获取obj.属性 不存在则报错
getattr(obj,'属性','默认值') #获取obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性

三 __setattr__,__delattr__,__getattr__

obj点的方式去操作属性时触发的方法

三个参数,给对象添加属性

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

__delattr__   删除属性的时候会触发

__getattr__  重点: 只有在使用点调用属性且属性不存在的时候才会触发

作用:系统内置函数属性(你定义了就用你定义的函数属性,不定义就用系统默认的函数属性)

 # 1、__getattr__  当没有内置属性,方法的时候会触发__getattr__
class Foo:
x = 1
def __init__(self,y):
self.y =y
def __getattr__(self, item):
print("get")
f1 = Foo(2)
print(f1.y) #
print(getattr(f1,"y")) # 相当于str ---> str.__len__()
f1.ss # do 当没有内置属性,方法的时候会触发__getattr__ # 2 delete 直接触发 __delattr__
class Foo:
x = 1
def __init__(self,y):
self.y =y
def __delattr__(self, item):
print("delete")
f1 = Foo(2)
print(f1.y) # del f1.y # delete 直接触发 __delattr__
del f1.x # delete # 3 添加/修改属性会触发__setattr__的执行
class Foo:
x = 1
def __init__(self,y):
self.y =y
def __setattr__(self, key, value):
print("set")
# self.key = value 会陷入无限的递归
self.__dict__[key] = value
f1 = Foo(2)
print(f1.__dict__)
f1.z = 2
print(f1.__dict__)

三种内置方法

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

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

 class List(list):
def append(self, p_object):
'我改写的方法'
if not isinstance(p_object,str):
print('只有字符串类型能被添加到列表中')
return
# self.append(p_object) #进入无限递归
super().append(p_object)
def show_mid(self):
'我新增的方法'
index=int(len(self)/2)
print(self[index]) l1=List('hello') l1.append('abc')
print(l1,type(l1)) #数字无法添加成功
l1.append(1)
print('-->',l1) #基于标准类型新增了功能
l1.show_mid()

二次加工标准类型

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

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

 import time
class Filehadle:
def __init__(self,filename,mode = "r",encoding = "utf-8" ):
self.file = open(filename,mode,encoding=encoding)
self.mode = mode
self.encoding = encoding
def write(self,line):
print("-------",line)
t = time.strftime("%Y-%m-%d %X")
self.file.write("%s %s" %(t,line))
def __getattr__(self, item):
print("触发getattr")
print(item,type(item))
# self.file.read
return getattr(self.file,item)
f1 = Filehadle("b.txt","w+")
f1.write("hello\n")
# f1.write("hello world")
f1.seek(0)
print(f1.read()) # 触发__getattr__ 没有的时候触发
f1.close()

授权示范

五 __setitem__,__getitem,__delitem__

#__getitem__,__setitem_,__delitem__
obj[‘属性’]的方式去操作属性时触发的方法
__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发
 #   ########################### 只适用于操作字典 ###############
class Func:
def __getitem__(self, item):
print("执行getitem")
return self.__dict__[item] def __setitem__(self, key, value):
print("执行setitem")
self.__dict__[key]=value def __delitem__(self, key):
print("执行delitem")
self.__dict__.pop(key) f1 = Func()
print(f1.__dict__)
f1["name"] = "xiaoming"
f1["age"] = 18
print("====>",f1.__dict__) del f1["name"]
print(f1.__dict__) print(f1["age"])

三种item操作

六 描述符(__get__,__set__,__delete__) 新式类中描述符在大型开发中常用

1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发

 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass

定义一个描述符

2 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

 class Foo:
def __get__(self, instance, owner):
print('触发get')
def __set__(self, instance, value):
print('触发set')
def __delete__(self, instance):
print('触发delete') #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
f1=Foo()
f1.name='egon'
f1.name
del f1.name
#疑问:何时,何地,会触发这三个方法的执行

引子:描述符类产生的实例进行属性操作并不会触发三个方法的执

 #描述符Str
class Str:
def __get__(self, instance, owner):
print('Str调用')
def __set__(self, instance, value):
print('Str设置...')
def __delete__(self, instance):
print('Str删除...') #描述符Int
class Int:
def __get__(self, instance, owner):
print('Int调用')
def __set__(self, instance, value):
print('Int设置...')
def __delete__(self, instance):
print('Int删除...') class People:
name=Str()
age=Int()
def __init__(self,name,age): #name被Str类代理,age被Int类代理,
self.name=name
self.age=age #何地?:定义成另外一个类的类属性 #何时?:且看下列演示 p1=People('alex',18) #描述符Str的使用
p1.name
p1.name='egon'
del p1.name #描述符Int的使用
p1.age
p1.age=18
del p1.age #我们来瞅瞅到底发生了什么
print(p1.__dict__)
print(People.__dict__) #补充
print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__) 描述符应用之何时?何地?

描述符应用之何时?何地?

3 描述符分两种
一 数据描述符:至少实现了__get__()和__set__()

二 非数据描述符:没有实现__set__()

4 注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

类属性>数据描述符
 #有get,set就是数据描述符,数据描述符比实例属性有更高的优化级

 class Foo:
def __get__(self,instance,owner):
print('===>get方法')
def __set__(self, instance, value):
print('===>set方法')
def __delete__(self, instance):
print('===>delete方法') class Bar:
x = Foo() # 调用foo()属性,会触发get方法 b1=Bar() #在自己的属性字典里面找,找不到就去类里面找,会触发__get__方法
b1.x #调用一个属性的时候触发get方法
b1.x=1 #为一个属性赋值的时候触发set方法
del b1.x #采用del删除属性时触发delete方法

数据描述符>实例属性

 #类属性>数据描述符>实例属性

 class Foo:
def __get__(self,instance,owner):
print('===>get方法')
def __set__(self, instance, value):
print('===>set方法')
def __delete__(self, instance):
print('===>delete方法') class Bar:
x = Foo() #调用foo()属性,会触发get方法 b1=Bar() #实例化
Bar.x=11111111111111111 #不会触发get方法
b1.x #会触发get方法 del Bar.x #已经给删除,所以调用不了!报错:AttributeError: 'Bar' object has no attribute 'x'
b1.x

类属性>数据描述符>实例属性

 #实例属性>非数据描述符
class Foo:
def __get__(self,instance,owner):
print('===>get方法') class Bar:
x = Foo() b1=Bar()
b1.x=1
print(b1.__dict__) #在自己的属性字典里面,{'x': 1}

实例属性>非数据描述符

 class Foo:
def __get__(self,instance,owner):
print('===>get方法') class Bar:
x = Foo()
def __getattr__(self, item):
print('------------>') b1=Bar()
b1.xxxxxxxxxxxxxxxxxxx #调用没有的xxxxxxx,就会触发__getattr__方法

非数据描述符>找不到

5 描述符使用

众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

A.让我么先来学习一下类的装饰器

 def deco(obj):
print(">>>>>",obj)
obj.x = 1
obj.y = 2
obj.z = 3
return obj
@deco # Foo = deco(Foo)
class Foo:
pass
print(Foo.__dict__)

1、类的装饰器基本原理

 # 参数版的装饰器
def Type(**kwargs):
def deco(obj):
for key,val in kwargs.items():
setattr(obj,key,val)
print("====>",kwargs)
print("类名",obj)
return obj
print(">>>",kwargs)
return deco
@Type(x= 1,y = 2) # Type(x=1,y=2) ---->@deco ---->Foo = deco(Foo)
class Foo:
pass
print(Foo.__dict__)

带参数的类的装饰器

 def Typed(**kwargs):
def deco(obj):
print(">>>",obj)
for key,val in kwargs.items():
setattr(obj,key,val)
return obj
return deco @Typed(x=1) # Typed(x=1) >>>> @deco 2 @deco ----> Foo = deco(Foo)
class Foo:
pass
print(Foo.__dict__)

3类的装饰器终极版

B.实现类型限制功能

 class Typed:
# def __init__(self,key):
# self.k = key
def __get__(self, instance, owner):
print("get方法")
print("instance参数【%s】" %instance) # instance参数【<__main__.People object at 0x0000020661BF4438>】
print("owner参数【%s】" % owner) # owner参数【<class '__main__.People'>】
def __set__(self, instance, value):
print("set方法")
print("instance参数【%s】" % instance) #instance参数【<__main__.People object at 0x0000020661BF4438>】
print("value参数【%s】" % value) #value参数【alex】 def __delete__(self, instance):
print("set方法")
print("instance参数【%s】" % instance) class People:
name = Typed()
def __init__(self,name,age,slary):
self.name =name
self.age = age
self.slary = slary
p1 = People("alex",36,743782.3321) # 触发 __set__
p1.name # 触发__get__
p1.name = "xiaoming"
print(p1.__dict__) # {'slary': 743782.3321, 'age': 36}

初探

 class Typed:
def __init__(self,key,expect_type):
self.key = key
self.expect_type = expect_type
def __get__(self, instance, owner):
print("get方法")
return instance.__dict__[self.key]
def __set__(self, instance, value):
print("set方法")
# print("instance参数【%s】" % instance) #instance参数【<__main__.People object at 0x0000020661BF4438>】
# print("value参数【%s】" % value) #value参数【alex】
if not isinstance(value,self.expect_type):
raise TypeError(" %s 传入的不是%s"%(self.key,self.expect_type))
# return
instance.__dict__[self.key] = value
def __delete__(self, instance):
print("set方法")
# print("instance参数【%s】" % instance) class People:
name = Typed("name",str)
age = Typed("age",int)
def __init__(self,name,age,slary):
self.name =name
self.age = age
self.slary = slary
p1 = People("alex",36,743782.3321) # 触发 __set__
print(p1.__dict__)
print(p1.name)
# p1 = People(213,11,22)
print(p1.__dict__) # {'slary': 743782.3321, 'age': 36}

实现类型检测

 ######################## 类的装饰器和描述符应用结合 ####################

 class Typed:
def __init__(self,key,expect_type):
self.key = key
self.expect_type = expect_type
def __get__(self, instance, owner):
print("get方法")
return instance.__dict__[self.key]
def __set__(self, instance, value):
print("set方法")
if not isinstance(value,self.expect_type):
raise TypeError(" %s传入的不是%s"%(self.key,self.expect_type))
# return
instance.__dict__[self.key] = value
def __delete__(self, instance):
print("set方法") def Type(**kwargs):
def deco(obj):
print(">>>",obj)
for name,expect_type in kwargs.items():
setattr(obj,name,Typed(name,expect_type))
return obj
return deco
@Type(name = str ,age = int)
class People:
# name = Typed("name",str)
# age = Typed("age",int)
def __init__(self,name,age,slary):
self.name =name
self.age = age
self.slary = slary
p1 = People("alex",22,743782.3321) # 触发 __set__
print(p1.__dict__)
# p1 = People(213,11,22)
print(p1.__dict__) # {'slary': 743782.3321, 'age': 36}

高级版,类的装饰器和描述符结合

6 描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件

7 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

class Lazyproperty:
def __init__(self,func):
print(self) # <__main__.Lazyproperty object at 0x000002181F574470>
print("======>",func) # ======> <function Room.area at 0x0000014B6274F2F0>
self.func = func
def __get__(self, instance, owner):
print(self) # <__main__.Lazyproperty object at 0x000001DD19624470>
print("get")
print(instance) # <__main__.Room object at 0x0000027CC4054470>
print(owner) # <class '__main__.Room'>
return self.func(instance)
class Room:
def __init__(self,name,width,length):
self.name = name
self.width = width
self.length = length
# @property
@Lazyproperty # area = Lazyproperty(area) # 就相当于把area是Lazyproperty类的对象 # 实例化对象
def area(self):
return self.width * self.length
r1 = Room('厕所',1,2)
print(r1.area)

自定制@property

 # 手动添加版
class Lazyproperty:
def __init__(self,func):
# print(self) # <__main__.Lazyproperty object at 0x000001DBBA094518>
# print(func) # <function Room.area at 0x000001DBBA08F510>
self.func = func class Room:
def __init__(self,name,width,length):
self.name = name
self.width = width
self.length = length
@Lazyproperty # area = Lazypeoprety(area) 当于实例化Lazypeoprety
def area(self):
return self.width * self.length
# @property
# def area1(self):
# return self.width * self.length r1 = Room("cesuo",1,2)
print(r1.area) # <__main__.Lazyproperty object at 0x000001FF19504438>
print(r1.area.func) # <function Room.area at 0x000002BB3093F488>
print(r1.area.func(r1)) # 2 手动添加版

手动添加版

 class Lazyproperty:
def __init__(self,func):
# print(self) # <__main__.Lazyproperty object at 0x000001DBBA094518>
# print(func) # <function Room.area at 0x000001DBBA08F510>
self.func = func
def __get__(self, instance, owner):
print("get")
if instance is None:
return self
res = self.func(instance)
setattr(instance,self.func.__name__,res)
return res class Room:
def __init__(self,name,width,length):
self.name = name
self.width = width
self.length = length
@Lazyproperty # area = Lazypeoprety(area) 当于实例化Lazypeoprety
def area(self):
return self.width * self.length
@property
def area1(self):
return self.width * self.length r1 = Room("cesuo",1,2)
print(r1.area)
print(r1.__dict__)
print(r1.area)
print(r1.area)
print(r1.area) print(r1.area1)

实现延迟计算功能

七 再看property

一个静态属性property本质就是实现了get,set,delete三种方法

 class Foo:
@property
def AAA(self):
print('get的时候运行我啊') @AAA.setter
def AAA(self,value):
print('set的时候运行我啊') @AAA.deleter
def AAA(self):
print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA 用法一

用法一

 class Foo:
def get_AAA(self):
print('get的时候运行我啊') def set_AAA(self,value):
print('set的时候运行我啊') def delete_AAA(self):
print('delete的时候运行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

用法二

用法

 class Goods:

     def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8 @property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price @price.setter
def price(self, value):
self.original_price = value @price.deleter
def price(self):
del self.original_price obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
print(obj.price)
del obj.price # 删除商品原价

案例一

 #实现类型检测功能

 #第一关:
class People:
def __init__(self,name):
self.name=name @property
def name(self):
return self.name # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常 #第二关:修订版 class People:
def __init__(self,name):
self.name=name #实例化就触发property @property
def name(self):
# return self.name #无限递归
print('get------>')
return self.DouNiWan @name.setter
def name(self,value):
print('set------>')
self.DouNiWan=value @name.deleter
def name(self):
print('delete------>')
del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里
print(p1.name)
print(p1.name)
print(p1.name)
print(p1.__dict__) p1.name='egon'
print(p1.__dict__) del p1.name
print(p1.__dict__) #第三关:加上类型检查
class People:
def __init__(self,name):
self.name=name #实例化就触发property @property
def name(self):
# return self.name #无限递归
print('get------>')
return self.DouNiWan @name.setter
def name(self,value):
print('set------>')
if not isinstance(value,str):
raise TypeError('必须是字符串类型')
self.DouNiWan=value @name.deleter
def name(self):
print('delete------>')
del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里
p1.name=1

案例二

八 __getattribute__

 # ######################getattr##################
# class Foo:
# def __init__(self,x):
# self.x = x
# def __getattr__(self, item):
# print("执行getattr") # 如果对象没有实例调用不到执行
#
# f1 = Foo(10)
# print(f1.x)
# f1.ccc # 触发getattr 执行getattr

__getattr__

 # ######################### getattribute #################

 # class Foo:
# def __init__(self,x):
# self.x = x
# def __getattribute__(self, item):
# print("执行getattribute") # 不管有没有都会触发
#
# f1 = Foo(10)
# print(f1.x)
# f1.ccc

__getattribute__

 # ###############3 两者同时存在 #######################
class Foo:
def __init__(self,x):
self.x = x
def __getattr__(self, item):
print("执行getattr") # 如果对象没有实例调用不到执行
def __getattribute__(self, item):
print("执行getattribute")
raise AttributeError('抛出异常') f1 = Foo(10)
print(f1.x)
# f1.ccc

九 __str__,__repr__,__format__

改变对象的字符串显示__str__,__repr__

自定制格式化字符串__format__

 class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return "名字是 %s 年龄是%s"%(self.name,self.age) f1 = Foo("alex",18)
print(f1) # 就相当于执行 str(f1) -----> f1.__str__() class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
# def __str__(self):
# return "这是str"
def __repr__(self):
return "名字是 %s 年龄是%s"%(self.name,self.age) f1 = Foo("alex",18)
print(f1) # 就相当于执行 str(f1) -----> f1.__str__() # str函数或者print函数--->obj.__str__()
# repr或者交互式解释器--->obj.__repr__()
# 如果__str__没有被定义,那么就会使用__repr__来代替输出
# 注意:这俩方法的返回值必须是字符串,否则抛出异常

str 和repr

 # ############################### format ###############################3
# format_dic = {
# "ymd" :'{0.year}{0.mon}{0.day3}',
# "y:m:d" :'{0.year}:{0.mon}:{0.day3}',
# "m-d-y":'{0.mon}-{0.day3}-{0.year}'
# } # 利用字符串拼接搞成一个字典
#
# class Date:
# def __init__(self,year,mon,day):
# self.year = year
# self.mon = mon
# self.day = day
# def __format__(self, format_spec): # 执行format()的时候自动执行 d1.__format__
# print("执行format")
# if not format_spec or format_spec not in format_dic: # 判断format_spec 传的是否是空,和 传过来的参数是否在字典中
# format_spec = "ymd"
# fmt = format_dic[format_spec] # 取字典key值
# return fmt.format(self) # '{0.year}{0.mon}{0.day3}'.format(self=d1)
# d1 = Date(2016,12,26)
# while True:
# forma = input("请输入你要查看的格式q退出")
# if forma.lower() =="q" :
# break
# print(format(d1,forma))

format

十 __slots__

1.__slots__是什么?是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。

__slots__的作用:节省内存空间

 #  ##### __slots__ 省内存 ,只能创建自己定义的参数 #####
class Foo:
__slots__ = "name" f1 = Foo()
# print(f1.__dict__) #Foo' object has no attribute '__dict__'
print(f1.__slots__)
f1.name = "alex"
# print(f1.name) # f1.age = 18 # 报错

__slots__

十一 __next__和__iter__实现迭代器协议

一、什么是迭代器协议

1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)

2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)

3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。

二、python中强大的for循环机制

for循环的本质:循环所有对象,全都是使用迭代器协议。

(字符串,列表,元组,字典,集合,文件对象)这些都不是可迭代对象,只不过在for循环式,调用了他们内部的__iter__方法,把他们变成了可迭代对象

然后for循环调用可迭代对象的__next__方法去取值,而且for循环会捕捉StopIteration异常,以终止迭代。

 # ################### 迭代器协议 ##################
class Foo:
def __init__(self,n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n == 13 :
raise StopIteration("终止了")
self.n += 1
return self.n
f1 = Foo(10)
print(f1.__next__())
print(f1.__next__())
print(f1.__next__())
# print(f1.__next__()) # StopIteration: 终止了 for i in f1: # obj = iter(f1) ------>f1.__iter__()
print(i) # obj.__next()

迭代器协议

 class Fib:
def __init__(self):
self._a=0
self._b=1 def __iter__(self):
return self def __next__(self):
self._a,self._b=self._b,self._a + self._b
return self._a f1=Fib() print(f1.__next__())
print(next(f1))
print(next(f1)) for i in f1:
if i > 100:
break
print('%s ' %i,end='')

斐波那契数列

十二 __doc__ 文档描述信息

 class Foo:
'我是描述信息'
pass print(Foo.__doc__)

描述信息

 class Foo:
'我是描述信息'
pass class Bar(Foo):
pass
print(Bar.__doc__) #该属性无法继承给子类

该属性无法被继承

十三 __module__和__class__

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

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

 class C:

     def __init__(self):
self.name = ‘SB' lib/aa.py

创建lib/aa.py

 1 from lib.aa import C
2
3 obj = C()
4 print obj.__module__ #输出 lib.aa,即:输出模块
5 print obj.__class__ #输出 lib.aa.C,即:输出类

2、输出模块和输出类

十四  __del__

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

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

 # class Foo:
# def __init__(self,name):
# self.name = name
# def __del__(self):
# print("我执行了")
# f1 = Foo("alex")
# # del f1 # 删除实例会触发__del__
# del f1.name # 删除实例的属性不会触发__del__
# print("#################")
#
# # 程序运行完毕会自动回收内存,触发__del__
a = 1
b = 2
a,b =b,a
print(a)
print(b

示范

十五 __enter__和__exit__

1、操作文件写法

1 with open('a.txt') as f:
2   '代码块'

2、上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

 # class Open:
# def __init__(self,name):
# self.name = name
# def __enter__(self):
# print("执行enter")
# return self
# def __exit__(self, exc_type, exc_val, exc_tb):
# print("执行exit")
# # 会自动触发__enter__
# # 文件操作完之后会触发 __exit__
# with Open("a.text") as f :
# print(f)
# print(f.name) # __enter__ 的返回值赋值给f
# print(">>>>>")
# print(">>>>>")
#
# print("s".center(20,"*"))

上下文管理协议

3、执行代码块

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都会执行

 class Foo:
def __init__(self,name):
self.name=name def __enter__(self):
print('执行enter')
return self #2、拿到的结果是self,并赋值给f def __exit__(self, exc_type, exc_val, exc_tb): #4、触发__exit__,然后执行print()
print('执行exit')
print(exc_type)
print(exc_val)
print(exc_tb) with Foo('a.txt') as f: #1、with触发的是__enter__,拿到的结果是self并赋值给f;
print(f) #3、然后会执行with代码块,执行完毕后
print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad)
print(f.name)
print('')

4、有返回值

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

 class Foo:
def __init__(self,name):
self.name=name def __enter__(self):
print('执行enter')
return self
'''class、异常值、追踪信息'''
def __exit__(self, exc_type, exc_val, exc_tb): #2、有异常的时候,就会触发__exit__方法
print('执行exit')
print(exc_type)
print(exc_val)
print(exc_tb)
return True #3、没有return True就会报错,如果有return True异常自己吃了,不报异常 with Foo('a.txt') as f:
print(f)
print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad) #1、有异常的情况,他就会触发__exit__
print(f.name) #不执行这行,直接打印下面那行
print('') #4、最后打印这行

总结:

with obj as f:
      '代码块'

1.with obj ---->触发obj.__enter__(),拿到返回值

2.as f----->f=返回值、

3.with obj as f 等同于 f=obj.__enter__()

4.执行代码块
一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
二:有异常的情况下,从异常出现的位置直接触发__exit__
a:如果__exit__的返回值为True,代表吞掉了异常
b:如果__exit__的返回值不为True,代表吐出了异常
c:__exit__的的运行完毕就代表了整个with语句的执行完毕

用途:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接(TCP协议建连接、传输数据、关连接)和锁(进程,线程)的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处。

十六 __call__

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

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

 class Foo:
def __call__(self, *args, **kwargs):
print("实例执行 obj()")
f1 = Foo()
f1() # 实例执行 obj()
Foo()() # 实例执行 obj()

__call__

十七 metaclass

1、示例:

1 class Foo:
2 pass
3
4 f1=Foo() #f1是通过Foo类实例化的对象

python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)

上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?

 class Foo:
pass f1=Foo() print(type(f1)) #<class '__main__.Foo'>
print(type(Foo)) #类的类就是<class 'type'> class Bar:
pass
print(type(Bar)) #<class 'type'>

type

2、什么是元类?

元类是类的类,是类的模板

元类是用来控制如何创建类的,正如类是创建对象的模板一样

元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例Foo类是 type 类的一个实例)

type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

3、创建类的两种方式

1 class Foo:
2 pass
3 print(Foo)
4 print(Foo.__dict__)

方法一

 #type就是类的类,用type()实例化的结果,就是产生一个类
#用type和class生成的类是一样的效果。 def __init__(self,name,age):
self.name=name
self.age=age def test(self):
print('=======执行的是test=====>') FFo=type('FFo',(object,),{'x':1,'__init__':__init__,'test':test})
print(FFo)
# print(FFo.__dict__) f1=FFo('alex',18)
print(f1.x) #调的是类属性
print(f1.name) #调name
f1.test() #调方法

方法二

4、一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类。

示例:  自定制元类

 class MyType(type):
def __init__(self,a,b,c):
print('元类的构造函数执行') def __call__(self, *args, **kwargs):
obj=object.__new__(self) #object.__new__(Foo)-->f1
self.__init__(obj,*args,**kwargs) #Foo.__init__(f1,*arg,**kwargs)
return obj class Foo(metaclass=MyType): #Foo=MyType(Foo,'Foo',(),{})---》__init__
def __init__(self,name):
self.name=name #f1.name=name
f1=Foo('alex')

自定制元类

十八:异常处理

查看请点击:异常处理