转载请注明预见才能遇见的博客:http://my.csdn.net/
原文地址:https://blog.csdn.net/pcaxb/article/details/94551226
人工智能系列-Python系列(十)面向对象
目录
五、类的内置attr属性 __setattr__,__delattr__,__getattr__
1.isinstance(obj,cls)和issubclass(sub,super)
3.__setitem__,__getitem,__delitem__
3.描述符(__get__,__set__,__delete__)
一、面向对象与实例属性
1.类和对象初探
# 类:
# 把一类事物的相同特征和动作整合到一起就是类
# 类是一个抽象的概念
# 对象:基于类而创建的一个具体的事物,具体存在的,也就是特征和动作整合到一起
# 类:数据属性和函数
# 对象:数据属性
class Chinese:
'人---Class'
# 类属性
type = "person"
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
def eat(self):
print('HHH')
# 这里没有self ,对象去调用的时候会报错,因为对象调用会自动传self参数
# def getHeight(): # p1.getHeight(p1)
# print('height')
print(dir(Chinese))
# 查看类的属性字典
print(Chinese.__dict__) # 类的字典属性
print(Chinese.__dict__['type']) # person
print(Chinese.__name__) # Chinese 类的名字(字符串)
print(Chinese.__doc__) # 人---Class 类的文档字符串
print(Chinese.__module__) # __main__ 类定义所在的模块
# 实例化 调用__init__
p1 = Chinese('pca', 12)
print(p1.__dict__) # 对象的字典 {'name': 'pca', 'age': 12}
print(p1.type) # person 先到__init__中找,然后到外层找,类是最外层
print(p1.name) # pca
p1.eat()
# 实例可以访问类属性,类不能访问实例属性
实例可以访问类属性,类不能访问实例属性
2.类属性增、删、改、查
Chinese.height = 120 # 增
Chinese.height = 130 # 改
# del Chinese.height # 删除
print(Chinese.height) # 查
print(Chinese.__dict__)
# 修改类,就会修改所有的实例,找的是引用
# 给类添加函数
def getWidth(self, width):
print(width)
Chinese.getWidth = getWidth
Chinese.getWidth(p1, "宽度测试 类") # 宽度测试 类
p1.getWidth("宽度测试 对象") # 宽度测试 对象
修改类,就会修改所有的实例,找的是引用
3.实例属性的增删改查
# 查
# print(p1.__dict__) # {'name': 'pca', 'age': 12}
# print(p1.name)
# p1.eat()
# 增
p1.color = 1
# 不要在对象中这样添加函数,虽然代码没有问题
def getColor(self):
print(self.name)
p1.getColor = getColor
p1.getColor(p1) # pca
# 删除
p1.del_ = 333
print(p1.del_)
del p1.getColor
del p1.del_
# 改
# 不要修改底层的属性字典
# p1.__dict__['sex']='male'
# print(p1.__dict__)
# print(p1.sex)
p1.color = 2
print(p1.__dict__)
4. 对象与实例属性
Chinese.vi = "VI=="
p1.vi = "VI+++"
print(Chinese.vi, p1.vi) # VI== VI+++
# 类属性和实例属性重名,不会影响
类属性和实例属性重名,不会影响
5.属性扩展
dog = 1
class Dm:
'test'
dog = 2
def __init__(self, dog1):
self.dog = dog1
print(dog) # 1
dm = Dm(3) # 1
print(Dm.dog) # 2
print(dm.dog) # 3
二、静态、组合、继承
1.静态属性和方法,类方法
class C1:
name = 1
h = 2
def __init__(self, height):
self.height = height
@property # 静态属性
def get_height(self): # 访问类属性
return self.height * 2 * self.h
@classmethod # 类方法
def class_method(cls):
return cls.name
@staticmethod # 静态方法 类的工具包、不和类和实例绑定
def wash_body():
print(888)
# 静态属性
# 在函数上加property,让调用函数看起来像调用属性一样
c1 = C1(2)
print(c1.get_height)
# 类方法 @classmethod
print(C1.class_method()) # 1
# 静态方法 @staticmethod
# 类的工具包、不和类和实例绑定,不能使用类变量和实例变量
C1.wash_body() # 888
# 实例:可以访问类属性和方法、可以访问自己的属性,一般实例不定义自己的方法
# 类:可以访问类属性和方法
# 静态属性:可以方法类属性和方法,实例的属性和方法
# 类方法: 可以访问类属性和方法
# 静态方法:不能访问类属性和方法、也不能访问实例属性和方法
2.组合
# 多个小类组合大类
# class Hand:
# pass
#
# class Foot:
# pass
#
# class Trunk:
# pass
#
# class Head:
# pass
#
#
# class Person:
# def __init__(self,id_num,name):
# self.id_num=id_num
# self.name=name
# self.hand=Hand()
# self.foot=Foot()
# self.trunk=Trunk()
# self.head=Head()
3.继承
class Dad:
'这个是父类'
money = 10
def __init__(self, name):
print('爸爸')
self.name = name
def hit_son(self):
print('%s 正在打儿子' % self.name)
class Son(Dad): # 继承Dad父类
pass
# 如果重名,没有覆盖一说,自己有调用自己的,自己没有调用父类的
son = Son('pca') # 子类没有__init__会调用父类的__init__
print(son.money) # 10
son.hit_son()
如果重名,没有覆盖一说,自己有调用自己的,自己没有调用父类的
4.接口继承与归一化设计
import abc # 导入abc
class All_file(metaclass=abc.ABCMeta): # metaclass=abc.ABCMeta
# abstract 抽象
@abc.abstractmethod
def read(self):
pass
@abc.abstractmethod
def write(self):
pass
class Disk(All_file): # 必须实现读和写
def read(self):
print('disk read')
def write(self):
print('disk write')
dick = Disk()
5.继承顺序
# Python可以继承多个类
# 深度优先 从左边开始找,找完左边所有再找右边
# 广度优先 从左边开始找,但是不找左边的最顶层父类,然后接着找右边, 找完右边再找最顶层父类
# Python3 新式类,使用广度优先
class A:
def test(self):
print('A')
pass
class B(A):
# def test(self):
# print('B')
pass
class C(A):
# def test(self):
# print('C')
pass
class D(B):
# def test(self):
# print('D')
pass
class E(C):
# def test(self):
# print('E')
pass
class F(D, E):
# def test(self):
# print('F')
pass
f1 = F()
f1.test() # 经典类:F->D->B->A-->E-->
print(F.__mro__)
# (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# F-->D->B-->E--->C--->A新式类
# 为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
# 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
# 1.子类会先于父类被检查
# 2.多个父类会根据它们在列表中的顺序被检查
# 3.如果对下一个类存在两个合法的选择,选择第一个父类
6.子类中调用父类方法
class Vehicle:
Country = 'China'
def __init__(self, name, speed, load, power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print('开动啦')
print('开动啦')
class Subway(Vehicle):
def __init__(self, name, speed, load, power, line):
# Vehicle.__init__(self, name, speed, load, power)
super().__init__(name, speed, load, power)
self.line = line
def show_info(self):
print(self.name, self.speed, self.load, self.power, self.line)
def run(self):
# Vehicle.run(self)
super().run()
print('%s %s 线,开动啦' % (self.name, self.line))
line13 = Subway('北京地铁', '10km/s', 1000000000, '电', 13)
line13.show_info()
line13.run()
三、多态、封装
1.多态
# 多态:多态指的是一类事物有多种形态
# 运行时候的绑定状态,比如len函数
# 不同的类继承父类有了相同的方法,实例化这些类,然后实例去调用这些相同的方法
# 就是继承的一种体现方式
# 类的继承有两层意义:改变 / 扩展
# 多态就是类的这两层意义的一个具体的实现机制,即,调用不同的类实例化得对象下的相同的方法,实现的过程不一样
2.封装
# 类中定义私有的,只在类的内部使用,外部无法访问
# 封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),
# 只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,
# 则代码改变不足为虑。
# 约定
# _开头:任何以单下划线开头的名字都应该是内部的,私有的, python 约定 下划线 _ 开头的就是私有的,只有内部使用,但是python不会限制调用,只是约定
# __开头:双下划线开头,看起来外部不能被访问,但是可以访问, 只是被python重命名了
class Person:
_name = 'pca'
__age = 12
# 接口函数/或者叫访问函数:暴露私有属性
def get_name(self):
return self._name
def get__age(self):
return self.__age
p = Person()
# {'__module__': '__main__', '_name': 'pca', '_Person__age': 12, '__dict__':
# <attribute '__dict__' of 'Person' objects>, '__weakref__':
# <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(Person.__dict__)
print(p._name) # pca
print(p._Person__age) # 12
print(p.get_name())
print(p.get__age())
四、反射
1.定义
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
2.实现反射的函数 适用于类和对象
# 1)hasattr(object, name) 判断object中有没有一个name字符串对应的方法或属性
# 2)getattr(object, name, default=None)
# 3)setattr(x, y, v)
# 4)delattr(x, y)
class BlackMedium:
feture = 'Ugly'
def __init__(self, name, addr):
self.name = name
self.addr = addr
def sell_hourse(self):
print('【%s】 正在卖房子' % self.name)
def rent_hourse(self):
print('【%s】 正在租房子' % self.name)
bm = BlackMedium('NAME', 'ADDRESS')
#判断是否存在数据属性和函数属性
print(hasattr(BlackMedium, 'feture')) # True
print(hasattr(bm, 'rent_hourse')) # True
#获取数据属性和函数属性
print(getattr(bm,'name')) # NAME 没有就会报错
print(getattr(bm,'name111','NAME111')) # NAME111 没有会使用默认
getattr(bm,'sell_hourse')() # 【NAME】 正在卖房子
# 设置数据属性和函数属性
setattr(bm,'age',12) #新增 不同名新增
setattr(bm,'name','NAMECC') # 修改 同名修改
print(bm.__dict__) # {'name': 'NAME', 'addr': 'ADDRESS', 'age': 12}
setattr(bm,'func',lambda x:x+1)
print(bm.func(5)) # 6
print(bm.__dict__)
# 删除 数据属性和函数属性
del bm.age
delattr(bm,'name')
# delattr(bm,'func')
print(bm.__dict__)
3.优点
# 实现可插拔机制
# 动态导入模块(基于反射当前模块成员)
# 动态导入模块
#非动态导入
# from demo import day
# day.day() #day=====day
#动态导入
# 1)获取顶级
demo = __import__('demo.day')
#不管你是多少层,这里的demo都是顶层
print(demo) # <module 'demo' from 'D:\\dev\\day24\\demo\\__init__.py'>
demo.day.day()#day=====day
# 2)获取下级
import importlib
demo2 = importlib.import_module('demo.day')
# <module 'demo.day' from 'D:\\dev\\day24\\demo\\day.py'>
print(demo2)
demo2.day()
#扩展 私有
from demo.day import *
day01()
# day02() # day01加了下划线,所以不能这样访问
# _day02() # day02====
from demo import day
day.day01()
day._day02() # day02====
五、类的内置attr属性 __setattr__,__delattr__,__getattr__
参考资料:https://www.cnblogs.com/linhaifeng/articles/6204014.html#_label3
# 设置、获取、删除对象属性的中间操作
class Foo:
x = 1
def __init__(self, y):
self.y = y
# 只有在使用点调用属性且属性不存在的时候才会触发重写的__getattr__
def __getattr__(self, item):
print("__getattr__")
# 添加/修改属性会触发它的执行
def __setattr__(self, key, value):
print("__setattr__")
# self.key = value #这就无限递归了
self.__dict__[key] = value # 应该使用它
def __delattr__(self, item):
print("__delattr__")
# del self.item # 无限递归了
self.__dict__.pop(item)
1.__setattr__
f1 = Foo(5) # __setattr__
f1.x = 1
# 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,
# 否则永远无法赋值
# print(f1.__dict__) # {}
print(f1.__dict__) # {'y': 5, 'x': 1}
2.__getattr__
print(f1.x) # 1 不会调用
# __getattr__只有在使用点调用属性且属性不存在的时候才会触发重写的__getattr__
f1.xxx # __getattr__会调用
3.__delattr__
f1.__dict__['y'] = 3 # 我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.x # __delattr__
print(f1.__dict__) # {'y': 3}
4.内置属性打印了解
class P:
pass
print(dir(P)) # 打印内置属性全
print(P.__dict__) # 打印内置属性不全
六、包装和授权
# 包装:二次加工标准类型
# 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。
# 授权:授权是包装的一个特性
# 授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
# 包装:包装的关键点就是继承实现
案例list只能append字符串
class List(list):
def append(self, item):
if type(item) is str:
super().append(item)
else:
print("只能添加字符串")
t2 = List()
t2.append("111")
t2.append(1)
print(t2)
授权:授权的关键点就是覆盖__getattr__方法
import time
class FileHandle:
def __init__(self,filename,mode='r',encoding='utf-8'):
# self.filename=filename
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(item,type(item))
# self.file.read
return getattr(self.file,item)
f1=FileHandle('a.txt','w+')
# print(f1.file)
# print(f1.__dict__)
# print('==>',f1.read) #触发__getattr__
# print(f1.write)
f1.write('cpu负载过高\n')
f1.write('内存剩余不足\n')
f1.write('硬盘剩余不足\n')
# f1.seek(0)
# print('--->',f1.read())
七:面向对象进阶
参考资料:https://www.cnblogs.com/linhaifeng/articles/6204014.html#_label3
1.isinstance(obj,cls)和issubclass(sub,super)
# isinstance(obj,cls)obj是否是类 cls 的对象
class Foo(object):
pass
obj = Foo()
print(isinstance(obj, Foo)) # True
# issubclass(sub,super)检查sub类是否是 super 类的派生类(子类)
class Bar(Foo):
pass
print(issubclass(Bar, Foo)) # True
bar = Bar()
print("isinstance:", isinstance(bar, Foo)) # isinstance: True
2.__getattribute__
class Per:
def __init__(self, x):
self.x = x
def __getattr__(self, item):
print("__getattr__")
def __getattribute__(self, item):
print("__getattribute__")
raise AttributeError('没有找到')
# raise 抛出异常
p1 = Per(1)
# print(p1.x)
p1.x
p1.y #
# 1)如果只是重写__getattr__,属性不存在时调用__getattr__
# 2)如果__getattribute__ 和 __getattr__都重写,不管属性存不存在,都只调用__getattribute__
# 如果想调用__getattr__ ,需要在__getattribute__设置 raise AttributeError('没有找到')
3.__setitem__,__getitem,__delitem__
# 点. 的方式操作属性:__setattr__,__delattr__,__getattr__
# 中括号[] 的方式操作属性:__setitem__,__getitem,__delitem__
# 这两种方式获取的结果一样
class Person:
def __init__(self, x):
self.x = x
def __setitem__(self, key, value):
print("__setitem__")
def __getitem__(self, item):
print("__getitem__")
def __delattr__(self, item):
print("__delattr__")
print("======================================================")
p1 = Person(10)
p1.x #
p1["x"] # __getitem__
p1["xy"] # __getitem__ 找不到也会调用__getitem__
4.__str__,__repr__,__format__
format_dict = {
'nat': '{obj.name}-{obj.addr}-{obj.type}', # 学校名-学校地址-学校类型
'tna': '{obj.type}:{obj.name}:{obj.addr}', # 学校类型:学校名:学校地址
'tan': '{obj.type}/{obj.addr}/{obj.name}', # 学校类型/学校地址/学校名
}
class School:
def __init__(self, name, addr, type):
self.name = name
self.addr = addr
self.type = type
def __repr__(self):
print("__repr__")
return 'School(%s,%s)' % (self.name, self.addr)
# def __str__(self):
# print("__str__")
# return '(%s,%s)' %(self.name,self.addr)
def __format__(self, format_spec):
print("__format__")
if not format_spec or format_spec not in format_dict:
format_spec = 'nat'
fmt = format_dict[format_spec]
return fmt.format(obj=self)
s1 = School('oldboy1', '北京', '私立')
# str函数或者print函数--->obj.__str__()
# repr或者交互式解释器--->obj.__repr__()
# 如果__str__没有被定义,那么就会使用__repr__来代替输出,如果定义了__str__,就会调用__str__
# 调用过程:print --->str(f1)---》f1.__str__()------>f1.__repr__()
# 注意:这俩方法的返回值必须是字符串,否则抛出异常
print("======================================================")
str(s1) # __str__
print(s1) # __str__ #过程:print --->str(f1)--->f1.__str__()
repr(s1) # __repr__ #过程:print --->str(f1)---》f1.__str__()------>f1.__repr__()
print(format(s1, 'nat')) # s1.__format__()
print(format(s1, 'tna'))
print(format(s1, 'tan'))
print(format(s1, 'asfdasdffd'))
# str函数或者print函数--->obj.__str__()
# repr或者交互式解释器--->obj.__repr__()
# 如果__str__没有被定义,那么就会使用__repr__来代替输出,如果定义了__str__,就会调用__str__
# 调用过程:print --->str(f1)---》f1.__str__()------>f1.__repr__()
# 注意:这俩方法的返回值必须是字符串,否则抛出异常
5.__slots__使用
'''
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。
'''
class Foo:
__slots__ = 'x'
f1 = Foo()
f1.x = 1
# f1.y = 2 # 报错
print(f1.__slots__) # f1不再有__dict__
class Bar:
__slots__ = ['x', 'y']
n = Bar()
n.x, n.y = 1, 2
# n.z = 3 # 报错
# print(n.__dict__) #报错
print(
Bar.__dict__) # {'__module__': '__main__', '__slots__': ['x', 'y'],
# 'x': <member 'x' of 'Bar' objects>, 'y':
# <member 'y' of 'Bar' objects>, '__doc__': None}
print(n.__slots__) # ['x', 'y']
print(Bar.__slots__) # ['x', 'y']
6.doc
class Foo:
'我是描述信息'
pass
print(Foo.__doc__) # 我是描述信息
class Foo:
'我是描述信息'
pass
class Bar(Foo):
pass
print(Bar.__doc__) # None 该属性无法继承给子类
7.__module__和__class__
# __module__ 表示当前操作的对象在那个模块
# __class__ 表示当前操作的对象的类是什么
from demo.day import Day
c1 = Day()
print(c1.__module__) # demo.day 表示当前操作的对象在那个模块
print(c1.__class__) # <class 'demo.day.Day'>
8.__del__ 析构方法
# 析构方法,当对象在内存中被释放时,自动触发执行。
# 注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,
# 即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__
class Foo:
def __init__(self,name):
self.name = name
def __del__(self):
print('执行我啦')
f1=Foo("111")
# del f1 # 删除对象,会调用释放内存,调用__del__
del f1.name # 删除属性,不会调用释放内存,不调用__del__
print('------->')
# 文件执行完释放内存 调用__del__
9.__call__
# 对象后面加括号,触发执行
# 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
八、迭代器协议 和 描述符
1. __next__和__iter__实现迭代器协议
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
# l=list('hello')
# for i in l:
# print(i)
f1 = Foo(10)
# print(f1.__next__())
# print(f1.__next__())
# print(f1.__next__())
# print(f1.__next__())
# for i in f1: # obj=iter(f1)------------>f1.__iter__()
# print(i) #obj.__next_()
2.迭代器协议实现 斐波那契数列
# 1 1 2 3 5 8 13 21
class C:
count = 0
def __init__(self, n):
self.m = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.count == 0:
self.count = 1
return 1
self.m, self.n = self.n, self.n + self.m
if self.n > 100:
raise StopIteration("停止")
return self.n
c1 = C(1)
for item in c1:
print(item)
3.描述符(__get__,__set__,__delete__)
1)描述符定义
描述符定义:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
# __get__():调用一个属性时,触发
# __set__():为一个属性赋值时,触发
# __delete__():采用del删除属性时,触发
class Foo0: # 在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
2)描述符作用
描述符作用:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
class Foo1:
def __get__(self, instance, owner):
print('触发get')
def __set__(self, instance, value):
print('触发set')
def __delete__(self, instance):
print('触发delete')
# 包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
f1 = Foo1()
f1.name = 'egon'
f1.name
del f1.name
class Person:
foo1 = Foo1()
def __init__(self, foo1):
self.foo1 = foo1
p1 = Person('chy')
p1.foo1
# del p1.foo1
print(p1.__dict__) #{}
print(Person.__dict__) #'foo1': <__main__.Foo1 object at 0x000001F64E02D470>,
print(type(p1) == Person) #True type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == Person.__dict__) #True
3)描述符 分类
# 数据描述符:至少实现了__get__()和__set__()
# 非数据描述符:没有实现__set__()
4)注意事项
# A 描述符本身应该定义成新式类,被代理的类也应该是新式类
# B 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
# C 要严格遵循该优先级,优先级由高到底分别是
# a.类属性
# b.数据描述符
# c.实例属性
# d.非数据描述符
# e.找不到的属性触发__getattr__()
参考资料:https://www.cnblogs.com/linhaifeng/articles/6204014.html#_label3
4.案例:检测数据类型
class Typed:
def __init__(self,key,expected_type):
self.key=key
self.expected_type=expected_type
def __get__(self, instance, owner):
print('get方法')
# print('instance参数【%s】' %instance)
# print('owner参数【%s】' %owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
print('set方法')
# print('instance参数【%s】' % instance)
# print('value参数【%s】' % value)
# print('====>',self)
if not isinstance(value,self.expected_type):
# print('你传入的类型不是字符串,错误')
# return
raise TypeError('%s 传入的类型不是%s' %(self.key,self.expected_type))
instance.__dict__[self.key]=value
def __delete__(self, instance):
print('delete方法')
# print('instance参数【%s】' % instance)
instance.__dict__.pop(self.key)
class People:
name=Typed('name',str) #t1.__set__() self.__set__()
age=Typed('age',int) #t1.__set__() self.__set__()
def __init__(self,name,age,salary):
self.name=name
self.age=age
self.salary=salary
# pe1=People('alex','13',13.3)
pe1=People("pca",13,13.3)
print(pe1.__dict__) # {'name': 'pca', 'age': 13, 'salary': 13.3}
九、上下文管理协议
1. __enter__和__exit__
class Foo:
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')
print(exc_type)
print(exc_val)
print(exc_tb)
return True
with Foo('a.txt') as f:
print(f)
print(asdfsaasdfasdfasdfasdfasfasdfasdfasdfasdfasfdasfd) #触发__exit__
print(f.name)
print('-----------------')
print('000000000000000000000000000000000000000000000')
2.with obj as f 执行过程
# with obj as f:
# '代码块'
# 1).with obj - ---》触发obj.__enter__(), 拿到返回值
# 2).as f - ---->f = 返回值、
# 3).with obj as f 等同于 f=obj.__enter__()
# 4).执行代码块
# A:没有异常的情况下,整个代码块运行完毕后去触发__exit__, 它的三个参数都为None
# B:有异常的情况下,从异常出现的位置直接触发__exit__
# a:如果__exit__的返回值为True,代表吞掉了异常
# b:如果__exit__的返回值不为True,代表吐出了异常
# c:__exit__的的运行完毕就代表了整个with语句的执行完毕
十、类的装饰器
1.类的装饰器理解
def Typed(**kwargs):
def deco(obj):
for key, val in kwargs.items():
# obj.key=val
setattr(obj, key, val)
return obj
return deco
@Typed(x=1, y=2, z=3) # 1.Typed(x=1,y=2,z=3) --->deco 2.@deco---->Foo=deco(Foo)
class Foo:
pass
print(Foo.__dict__)
2.类的装饰器的应用
# class Typed:
# def __init__(self,key,expected_type):
# self.key=key
# self.expected_type=expected_type
# def __get__(self, instance, owner):
# print('get方法')
# # print('instance参数【%s】' %instance)
# # print('owner参数【%s】' %owner)
# return instance.__dict__[self.key]
# def __set__(self, instance, value):
# print('set方法')
# # print('instance参数【%s】' % instance)
# # print('value参数【%s】' % value)
# # print('====>',self)
# if not isinstance(value,self.expected_type):
# # print('你传入的类型不是字符串,错误')
# # return
# raise TypeError('%s 传入的类型不是%s' %(self.key,self.expected_type))
# instance.__dict__[self.key]=value
# def __delete__(self, instance):
# print('delete方法')
# # print('instance参数【%s】' % instance)
# instance.__dict__.pop(self.key)
#
# def deco(**kwargs): #kwargs={'name':str,'age':int}
# def wrapper(obj): #obj=People
# for key,val in kwargs.items():#(('name',str),('age',int))
# setattr(obj,key,Typed(key,val))
# # setattr(People,'name',Typed('name',str)) #People.name=Typed('name',str)
# return obj
# return wrapper
# @deco(name=str,age=int) #@wrapper ===>People=wrapper(People)
# class People:
# name='alex'
# # name=Typed('name',str)
# # age=Typed('age',int)
# def __init__(self,name,age,salary,gender,heigth):
# self.name=name
# self.age=age
# self.salary=salary
# # p1=People('213',13.3,13.3,'x','y')
# print(People.__dict__)
3.自定制property
class Lzp:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
print("get")
# 如果是对象调用,instance是Room对象,如果是类调用instance是None
# print(instance)
if instance is None:
return self
res = self.func(instance)
#
setattr(instance, self.func.__name__, res)
return res
class Room:
def __init__(self, width, height):
self.width = width
self.height = height
@Lzp # count_wh = Lzp(count_wh) Lzp 实例化
def count_wh(self):
return self.width * self.height
@property
def count_wh_2(self):
return self.width * self.height * 2
room = Room(2, 4)
print(room.count_wh_2) # 16
print(Room.count_wh_2) # <property object at 0x000001AD5427CF48>
print(room.count_wh) # 8
print(Room.count_wh) # <__main__.Lzp object at 0x00000191CA31D470>
print(room.__dict__)
print(room.count_wh) # 8 # 不会调用get ,实例属性,大于非数据描述符
# print(Room.count_wh)
4.property 补充
# class Foo:
# @property
# def AAA(self):
# print('get的时候运行我啊')
#
# @AAA.setter
# def AAA(self,val):
# print('set的时候运行我啊',val)
# @AAA.deleter
# def AAA(self):
# print('del的时候运行我啊')
# #只有在属性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,val):
print('set的时候运行我啊',val)
def del_AAA(self):
print('del的时候运行我啊')
AAA=property(get_AAA,set_AAA,del_AAA)
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
十一、元类
参考资料:https://www.cnblogs.com/linhaifeng/articles/8029564.html
1.元类的了解
# 元类:元类是类的类,是类的模板
# 元类是用来控制如何创建类的,正如类是创建对象的模板一样
class Per:
pass
per = Per()
print(type(per)) # <class '__main__.Per'>
print(type(Per)) # <class 'type'>
2.元类创建类
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
Foo = type("Foo", (object,), {"x": 1, '__init__': __init__,'get_name':get_name})
foo = Foo('pca')
print(Foo.__dict__)
print(foo.__dict__) # {'name': 'pca'}
print(Foo.x) # 1
print(foo.name) # pca
print(foo.get_name()) # pca
3.自定义元类
class MyType(type):
def __init__(self,a,b,c):
print('元类的构造函数执行')
# print(a)
# print(b)
# print(c)
def __call__(self, *args, **kwargs):
# print('=-======>')
# print(self)
# print(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
# print(Foo)
# f1=Foo('alex')
# print(f1)
f1=Foo('alex')
print(f1)
print(f1.__dict__)
参考资料:https://www.cnblogs.com/linhaifeng/articles/6182264.html
人工智能系列-Python系列(十)面向对象