饮冰三年-人工智能-Python-18Python面向对象
1 类与实例对方法和属性的修改
class Chinese: # 这是一个Chinese的类 #定义一个类属性,又称为静态数据或者静态变量,相当于C#中的static country="China" # 定义一个初始化函数, def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender #定义一个方法 def play(self,ballName): print('%s 正在打 %s' %(self.name,ballName)) #1类属性 print("----------开始修改类属性") #1.1 查看类属性 print(Chinese.country) #China #1.2 修改类属性 Chinese.country="中华人民共和国" print(Chinese.country) #中华人民共和国 #1.3 删除类属性 del Chinese.country print(Chinese.__dict__) #{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000001D30EA0>, 'play': <function Chinese.play at 0x000000000247C7B8>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None} #1.4 添加类属性 Chinese.country="中国" print(Chinese.__dict__) #{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000002300EA0>, 'play': <function Chinese.play at 0x000000000250C7B8>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'country': '中国'} #2方法属性 print("----------开始修改方法属性") #2.1 查看方法属性 p1=Chinese('张三',18,"男") Chinese.play(p1,'篮球') #张三 正在打 篮球 #2.2 修改方法属性 def play(self,ballName,address): print('%s 在%s打%s' %(self.name,address,ballName)) Chinese.play=play Chinese.play(p1,'篮球','操场') #张三 在操场打篮球 #2.3 删除类属性 del Chinese.play print(Chinese.__dict__) #{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000001D40EA0>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'country': '中国'} #2.4 添加方法 def eat(self,food): print("%s 正在吃 %s" %(self.name,food)) Chinese.eat=eat print(Chinese.__dict__) # {'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000001D40EA0>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'country': '中国', 'eat': <function eat at 0x0000000001DFC7B8>} #3实例类属性 print("----------开始修改实例类属性") #3.1 查看实例类属性 p=Chinese('张三',18,"男") print(p.country) #中国 #3.2 修改实例类属性 p.country="中华人民共和国" print(p.country) #中华人民共和国 #3.3 删除实例类属性 del p.country print(p.__dict__) #{'name': '张三', 'age': 18, 'gender': '男'} # 3.4 添加类属性 p.country="中国" print(p.__dict__) #{'name': '张三', 'age': 18, 'gender': '男', 'country': '中国'} print("----------开始修改实例属性") #4.1 查看实例类属性 p=Chinese('张三',18,"男") print(p.name) #张三 #4.2 修改实例类属性 p.name="张三三" print(p.name) #张三三 #4.3 删除实例类属性 del p.name print(p.__dict__) #{'age': 18, 'gender': '男'} # 4.4 添加类属性 p.name="张三三" print(p.__dict__) #{'age': 18, 'gender': '男', 'name': '张三三'} print("----------开始修改实例方法") #4.1 查看实例方法 p=Chinese('张三',18,"男") p.eat('包子') # 张三 正在吃 包子 #4.2 修改实例类属性 def eat(self,food,address): print('%s在%s吃%s' %(self.name,address,food)) p.eat=eat p.name="张三三" p.eat(p,"包子","食堂") #张三三在食堂吃包子 #4.3 删除实例类属性 del p.eat print(p.__dict__) #{'name': '张三三', 'age': 18, 'gender': '男'} # 4.4 添加实例方法属性 def study(self,cont): print('%s学%s' % (self.name,cont)) p.study = study p.study(p,'python') print(p.__dict__) #张三三学python # {'name': '张三三', 'age': 18, 'gender': '男', 'study': <function study at 0x0000000002526B70>}
类是什么时候被加载的,以及类名是什么时候生效的?
创建一个类的空间,然后从上到下加载内容(属性、方法)。然后把这个命名空间指向类名。
为什么Chinese.play与p.play的内存地址不一样?
p.play其实存的不是func的内存地址,而是存着func内存地址的一个变量的地址。类似:Chinese.play存的是你的家庭地址。而p.play存的是地址簿的第二页。
2 静态属性、静态方法、类方法
class Room: owner="China" # 这是一个房间的类 def __init__(self,name,width,length,height): self.name=name self.width=width self.length =length self.height=height @property # 这是一个方法,但是通过@property装饰后,就变成了一个静态方法,可以通过.方法名调用 def getArea(self): return self.width*self.length @classmethod # 这是一个方法,但是通过@classmethod装饰后,就变成了一个类方法,可以类调用,不用实例化 def getOwner(cls): print("房产所有者"+cls.owner) @staticmethod # 这是一个方法,但是通过@staticmethod装饰后,就变成了一个静态方法,可以在调用处直接传递参数 # 用户输入想要的面积和折扣,可以计算出价格 def getPrice(area,discount): return area*20000*discount/10 r1=Room("天字一号房",10,20,2) print('房间面积'+str(r1.getArea)) #房间面积200 #类方法:可以通过类名直接调用方法 Room.getOwner() #房产所有者China #静态方法,可以传递一些与类关联不大的数据 print(r1.getPrice(r1.getArea,9)) #3600000.0
总结:
静态属性 @property | 静态方法 @staticmethod | 类方法 @classmethod |
只读的,可以当属性使用 |
和类有关,和参数关系不密切 |
一个cls参数 |
实例.func |
实例.func() | 类.func(),类实例.func() |
3 组合(大类包含小类) 什么有什么的关系
class School: # 这是一个学校类 def __init__(self,name,address): self.name=name self.address=address class Courses: # 这是一个课程类,组合了一个学校 def __init__(self, name, cycle,price,schoolObj): self.name = name self.cycle = cycle self.price = price self.school = schoolObj def getInfo(self): print('%s 正在开设 %s' %(self.school.name,self.name)) school = School('北大','北京') Courses('python','100','10',school).getInfo()
4 继承(子类继承父类) 什么是什么的关系
#继承一:抽象出共有的父类,实现继承 class Animal: def __init__(self,name): self.name=name def eat(self): print('%s会吃' %(self.name)) def bark(self): print('%s会叫' %self.name) class Dog(Animal): pass class Cat(Animal): pass dog=Dog("小黄") dog.bark() cat = Cat("伊丽莎白") cat.eat() #继承二:但是猫和狗的叫声不同,所以常用的是接口继承(父类只提供一个规范,子类在调用时需要实现这些规范) # 如果是抽象方法,子类必须实现 import abc class Animal(metaclass=abc.ABCMeta): def __init__(self,name): self.name=name @abc.abstractclassmethod def eat(self): print('%s会吃' %(self.name)) def bark(self): print('%s会叫' %self.name) class Dog(Animal): def bark(self): print('%s会汪汪叫' %self.name) #如果是抽象方法,子类必须实现 def eat(self): pass class Cat(Animal): def eat(self): print('%s会吃鱼儿' %self.name) dog=Dog("小黄") dog.bark() cat = Cat("伊丽莎白") cat.eat()
4.1 继承顺序
当类是经典类(class C1:)时,多继承情况下,深度优先(依次找到根节点)
当类是新式类(class C1(object):)时,多继承情况下,广度优先(最后一次才找到根节点)
原理:python会计算一个方法解析顺序(MRO)列表,可通过print(类名.__mro__)查看
4.2 子类中调用父类的方法
class Vehicle: def __init__(self,name,speed,load): self.name=name self.speed = speed self.load = load def run(self): print('%s开始启动' %(self.name)) class Subway(Vehicle): def __init__(self,name,speed,load,line): Vehicle.__init__(self,name,speed,load) self.line = line def run(self): Vehicle.run(self) print("时速:"+self.speed) class Car(Vehicle): # 使用super减少了self字段的传递,而且如果父类类名修改的话,子类继承不用改变 def __init__(self,name,speed,load,brand): super().__init__(name,speed,load) self.brand = brand def run(self): super().run() print("品牌:"+self.brand) #测试数据 sub=Subway("地铁","300km/h","20000","2号线") sub.run() cr=Car("汽车","80km/h","5","红旗") cr.run()
5 多态
类的继承有两成意义:改变+扩展。多态就是累的这两层意义的一个具体的实现机制。在不考虑实例(子类)类型的情况下,使用子类。即调用不同的实例化对象下的相同方法,时间的过程不同
class H2O: def __init__(self,name,temp): self.name=name self.temp = temp def currentStatus(self): if self.temp < 0: print("在101.325kPa下,当前状态为:冰") elif self.temp == 0: print("在101.325kPa下,当前状态可能是:冰水混合物") elif self.temp < 100: print("在101.325kPa下,当前状态为:水") elif self.temp > 100: print("在101.325kPa下,当前状态为:水蒸气") class Ice(H2O): pass class Water(H2O): pass class Steam(H2O): pass ice = Ice("冰",-10) water = Water("水",10) steam= Steam("水蒸气",101) ice.currentStatus();water.currentStatus();steam.currentStatus();
6 封装 明确区分内外,其实不能真正不被外部访问。内部实现逻辑,外部无需知晓,并且为封装到内部的逻辑提供一个供外部访问的接口
约定:_开头定义
__开头,其实在外部调用的时候python默认修改了名“_类名_定义名“
class People: _plate="地球" def __init__(self,name,age): self.name=name self._age=age p=People("张三",12) print(p._plate) #地球 print(p._age) #12 '''虽然以_开头但是还可以被访问到,因为这只是一种约定''' class People2: __plate="地球" def __init__(self,name,age): self.name=name self.__age=age p=People2("张三",12) #print(p.__plate) #报错 AttributeError: 'People2' object has no attribute '__plate' print(p._People2__plate) #地球 print(p._People2__age) #12 '''虽然以__开头不可以被直接访问到,但可以通过“_+类名+自定义名称''' class People3: __plate="地球" def __init__(self,name,age): self.name=name self.__age=age # 外部现在是无法通过属性名调用了,但是目前有一个需求:就是要获取年龄,这个时候还可以通过定义接口的方法实现 def getAge(self): return self.__age; p3=People3("张三",12) print(p3.getAge())
没有必要的封装,不可取
7 反射:自省,自己检测自己。主要指程序可以访问、检测和修改它本身状态或行为的一种能力。
用字符串类型的变量名来访问变量值。类似XX.YY 这种形式,都可以通过反射来访问。类、对象、模块
# 反射:自省,自己检测自己。主要指程序可以访问、检测和修改它本身状态或行为的一种能力 class Animal: able="Run" def __init__(self,name): self.name=name def eat(self): print('%s会吃' %(self.name)) def bark(self): print('%s会叫' %self.name) a1=Animal("Dog") print(a1.name) print(a1.__dict__["name"]) # a1.name 等价于 a1.__dict__["name"] # 1 hasattr(object,name) 判断object中有没有一个name字符串对应的方法或属性 print(hasattr(a1,'able')) #True print(hasattr(a1,'eat')) #True print(a1.__dict__) #{'name': 'Dog'} # 虽然__dict__中没有eat方法,但是,这里只是检查能否调用到 # 2 getattr(object,name) 等价于 a1.属性或方法 获取对应的方法或属性 print(getattr(a1,'able')) #Run print(getattr(a1,'eat')) #<bound method Animal.eat of <__main__.Animal object at 0x0000000002564630>> # 如果没有就报错,通过默认参数的方法可以不报错 print(getattr(a1,'11111',"该属性不存在")) print(getattr(a1,'2222',"该方法不存在")) # 3 getattr(object,k,v) 等价于 a1.属性或方法='' 判断设置对应的方法或属性 setattr(a1,'able2','run') #创建 setattr(a1,'able','drink') #修改 print(getattr(a1,'able')) #drink print(getattr(a1,'able2')) #run # 4 delattr(object,k,v) 删除对应的方法或属性 delattr(a1,'able')
# 反射:自省,自己检测自己。主要指程序可以访问、检测和修改它本身状态或行为的一种能力 class Animal: able="Run" def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s会吃' %(self.name)) def bark(self): print('%s会叫' %self.name) def __getattr__(self, item): print("调用__getattr__(self, item)方法") # __setattr__()方法不常用,但可以通过重写该方法,实现自己特殊的需求,如类型只能是字符串 def __setattr__(self, key, value): if key.strip()=="name": if type(value) is str: self.__dict__[key] = value else: print("name必须是字符串") else: self.__dict__[key] = value def __delattr__(self,item): self.__dict__.pop(item) a1=Animal("Dog",12) # __开头表示内置属性 # 修改属性 a1.__setattr__('name',15) print(a1.__dict__) #{'name': 'Dog', 'age': 12} del a1.name #调用__delattr__(self, item)方法 # 只有在属性不存在的时候会触发__getattr__ print(a1.abccccc)
# 导入其他模块进行反射 import pkage1.say as p print(hasattr(p,"sayHello")) print(getattr(p,"sayHello111","不存在"))
# 反射可以使代码更灵活,易于扩展 # Why?因为反射可以根据字符串去获取、访问变量 class Manager: # 定义成操作列表,列表的优势是可以通过下标索引来访问 OPERATE_DIC = [ ('创建班级', 'create_class'), ('创建课程', 'create_course'), ('创建学生', 'create_student'), ] def __init__(self, name): self.name = name def create_class(self): print("创建班级") def create_course(self): print("创建课程") def create_student(self): print("创建学生") class Student: OPERATE_DIC = [ ('选择班级', 'choose_class'), ('选择课程', 'choose_course'), ] def __init__(self, name): self.name = name def choose_class(self): print("选择班级") def choose_course(self): print("选择课程") def login(): userName = input("账号:") pwd = input("密码:") with open("用户表") as f: for line in f: user, password, role = line.strip().split("|") if user == userName and password == pwd: return user, role else: print("用户不存在") def main(): user, role = login() if user and role: print("欢迎%s:%s" % (role, user)) # 根据用户角色反射出用户类--开始 # 1:自己文件反射需要引入包 import sys # 2:根据字符串去获取文件中的类(灵活的判断是Manager还是Student) cls = getattr(sys.modules["__main__"], role) # 3:根据类创建对象 obj = cls(user) op_dic = obj.OPERATE_DIC # 4: 有了对象,调用发方法 while True: #enumerate方法是将op_dic 组合为一个索引序列,同时列出数据和数据下标 for num,i in enumerate(op_dic,1): print(num,i) chooseNum= int(input("请输入操作序号:")) # 根据用户输入的操作序号,获取相应的操作方法名,字符串 op_fun = op_dic[chooseNum-1][1] #通过对象的反射,对象.方法名 ;调用 getattr(obj,op_fun)() main()
# 反射场景: # 表:网关、夜灯、呼叫器、上下线记录表 # 关系:一个网关下有多个夜灯、呼叫器,子表中通过(网关的id)关联网关表, # 所有的设备(网关、夜灯、呼叫器)的上下线记录都会存储在上下线记录表中(设备id,设备类型) # 我们根据记录表中的设备id要反向关联出是设备的详细信息,如何处理。 from deviceApp import models as TModel # 首先定义一个枚举类型(light【设备类型,网关表中存储的数据】,"NightLightDevice" Model类中的名字) # @unique # class DeviceType(Enum): # # 定义设备类型,根据设备类型对应不同 # light = "NightLightDevice" # gate = "GatewayDevice" # call = "CallbuttonDevice" # deviceType = request['deviceType'] # 设备产品 例如call # # 1. 根据deviceType关联枚举类型,利用反射获取相应的model类 # cls = getattr(TModel, DeviceType[deviceType].value, None) # if cls: # # 如果model类存在,根据主键id,找到对应的设备实体 # child_device_obj = cls.objects.get(id=deviceId) # else: # return failResultJson(msg='getattr-TModel对象失败')
8 二次加工标准类型
# 方法1,通过继承实现 class ListYK(list): # 添加一个新方法,求取中间值 def show_middle(self): mid_index=int(len(self)/2) return self[mid_index] # 重写一个添加方法,只能添加字符串 def append(self,p_object): if type(p_object) is str: # list.append(self,p_object) super().append(p_object) l1=ListYK("hello") print(l1.show_middle()) #l l1.append("1") l1.append(2) print(l1) #['h', 'e', 'l', 'l', 'o', '1'] # 2授权:授权时包装的一个特性,包装一个类型通常是对已存在的类型的一些定制 # 授权的过程,及时所有更新的功能都是有新类的某部分来处理,但已存在的功能就授权给队形的默认属性 import time class OpenNew: def __init__(self,filename,mode='r',encoding="utf-8"): self.file=open(filename,mode,encoding=encoding) self.filename=filename self.mode = mode self.encoding = encoding def __getattr__(self, item): return getattr(self.file,item) def write(self,line): t = time.strftime('%Y-%m-%d %T') self.file.write('%s %s' % (t, line)) f1=OpenNew("a.txt","w+","utf-8") f1.write("111\r\n") f1.write("222")
9 __new__
1 class Single: 2 def __new__(cls, *args, **kwargs): 3 obj = object.__new__(cls) #类名() 调用了new方法,开辟可一个空间给obj。 4 print("init中的self:", obj) 5 return obj 6 def __init__(self): 7 print("init中的self:",self) 8 sin=Single() 9 10 # 输出结果, 11 # init中的self: <__main__.Single object at 0x00CDA930> 12 # init中的self: <__main__.Single object at 0x00CDA930> 13 14 # 结论:line 8 :类名() 调用了new方法,开辟可一个空间给obj。 15 # 然后把obj的空间给self。# 16 # line 6 :执行__init__ 方法 17 # 最后把空间给调用者sin
class Single: ISINCTANCE=None def __new__(cls, *args, **kwargs): if not cls.ISINCTANCE: cls.ISINCTANCE = object.__new__(cls) return cls.ISINCTANCE def __init__(self, *args, **kwargs): pass a = Single() print(a) b = Single() print(b) ''' 输出结果 <__main__.Single object at 0x00F5FE70> <__main__.Single object at 0x00F5FE70> ''' # 结论,先定义属性,new的时候判断该属性是否有值,如果有值返回。没有值再new值
10 __getattribute__与__getattr__
#__getattribute__与__getattr__ # 1:只有__getattr__存在的情况下,只有获取的属性不存在,才触发此方法 class Foo: def __init__(self, name, age): self.name = name self.age = age def getInfo(self): return "My name is %s,I am %s years old" % (self.name, self.age) def __getattr__(self,item): print("我是__getattr") f = Foo("Mr.zhang", 18) f.name f.agea #只有属性不存在的时候,才会触发 class Foo2: def __init__(self, name, age): self.name = name self.age = age def getInfo(self): return "My name is %s,I am %s years old" % (self.name, self.age) def __getattribute__(self, item): print("我是__getattribute__") f2 = Foo2("Mr.zhang", 18) f2.name f2.agea #不管是否存在,都会触发__getattribute__方法 class Foo3: def __init__(self, name, age): self.name = name self.age = age def getInfo(self): return "My name is %s,I am %s years old" % (self.name, self.age) def __getattribute__(self, item): print("我是__getattribute__") raise AttributeError("属性不存在,就会触发getattr方法") def __getattr__(self, item): print("我是__getattr") print("F3=======>") f3 = Foo3("Mr.zhang", 18) f3.name f3.agea # 不管是否存在,都会触发__getattribute__方法,当都_getattribute__方法抛出AttributeError异常时,触发__getattr__方法
11 __item__
class Foo: def __init__(self, name, age): self.name = name self.age = age def getInfo(self): return "My name is %s,I am %s years old" % (self.name, self.age) def __getitem__(self, item): print("我是__getitem__方法") return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): self.__dict__.pop(self.__dict__[key]) f = Foo("Mr.zhang", 18) print(f["name"]) f["age"]="12" print(f["age"]) del f.age print(f.__dict__) ''' 我是__getitem__方法 Mr.zhang 我是__getitem__方法 12 {'name': 'Mr.zhang'} '''
12 __str__与__repr__
# 注意:这两个方法的返回值必须是字符串 # 如果__str__没有定义,那么就会__repr__替代 class Foo: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return ("我叫%s 今年%s岁了" %(self.name,self.age)) def __repr__(self): return ("我今年%s岁了 叫%s " % (self.age,self.name)) f=Foo("杨可",26) print(f) #str函数或者print函数 --》obj.__str__() #repr或者交互式解释器--》obj.__repr__() #如果__str__没有被定义,那么就会使用__repr__来代替
13 __len__
class Student: def __len__(self): return len(self.name) def __init__(self,name): self.name=name st=Student("张三") print(len(st)) st=Student("尼古拉斯赵四儿") print(len(st))
14 __hash__
class Employee: def __init__(self, name, sex, age, dept): self.name = name self.sex = sex self.age = age self.dept = dept def __hash__(self): return hash('%s%s' % (self.name, self.sex)) def __eq__(self, other): if self.name == other.name and self.sex == other.sex: return True employ_lst = [] for i in range(1000): if i % 3 == 0: employ_lst.append(Employee('张三', 'male', i, 'python')) elif i % 3 == 1: employ_lst.append(Employee('李四', 'male', i, 'python')) else: employ_lst.append(Employee('王五', 'male', i, 'python')) employ_newList = set(employ_lst) for i in employ_newList: print(i.name, i.sex, i.age) ''' 1:定义一个员工类(Employee) 2:对象的属性 : 姓名 性别 年龄 部门 3:重写__hash__方法 4:重写__eq__方法 5:初始化数据 6:set去重 ''' '''输出结果 李四 male 1 张三 male 0 王五 male 2 '''
15 查看实例的来源
from pkage1.Foo import Foo f = Foo() print(f.__module__) print(f.__class__) # pkage1.Foo # <class 'pkage1.Foo.Foo'>
16 析构函数
class Foo: def __init__(self,name): self.name=name def __del__(self): print("析构函数") f = Foo("zhangsan") del f
17 __call__ 利用 ()可以执行
class Foo: def __call__(self, *args, **kwargs): print("实例执行了call方法") f=Foo() f() # 实例执行了call方法
class Foo: def __call__(self, *args, **kwargs): print("实例执行了call方法") class Foooo: def __init__(self,cls): self.c=cls() #实例化传递过来的cls,也就是Foo self.c() # ()调用Foo中的__call__ 方法 Foooo(Foo)
18 迭代器协议
# 迭代器协议:有一个iter方法,有一个next方法,有Stopiteration终止 class Foo: def __init__(self,x,prv=0,cur=1): self.x=x self.prv = prv self.cur = cur def __iter__(self): return self def __next__(self): if self.x>100: raise StopIteration("越界了") self.x = self.prv + self.cur self.prv =self.cur self.cur= self.x return self.cur f =Foo(10) print(f.__next__()) print(next(f)) print("====>") for i in f: print(i) # 1 # 2 # ====> # 3 # 5 # 8 # 13 # 21 # 34 # 55 # 89 # 144
19 上下文管理协议
# 上下文管理协议 # with obj as f 等同于 f=obj.__enter__() # 执行代码块 # 一:没有异常,整个代码块运行完毕后触发__exit__,它的三个参数都为None # 二:出现异常,直接触发__exit__ # a:如果__exit__的返回值为True,代表吞掉了异常 # b:如果__exit__的返回值不为True,代表吐出来异常 # c:__exit__的运行完毕,代表整个with语句执行完毕 class OpenNew: def __init__(self,name): self.name = name def __enter__(self): print('执行enter') def __exit__(self, exc_type, exc_val, exc_tb): print("执行exit方法") print("3") print(aa) print(exc_type) print(exc_val) print(exc_tb) print("4") return True with OpenNew("a.txt") as f: print('1') print('2') print('5')
20 描述符
#描述符协议:描述符本质就是一个新式类,至少实现了__get__(),__set__(),__delete__()中的至少一个 # __get__(),调用属性时触发 # __set__(),为属性赋值是触发 # __delete__(),删除属性时触发 # 描述符作用:可以用来定义一个类的属性, # 描述符的特性:由该类产生的实例变化时候不会调用这些个方法,定义另外一个属性时,会被调用 # 描述符分类:没有实现__set__()的叫非数据描述符,实现了__set__()和__get__()的叫数据描述符 # 注意事项:1:描述符本身和被代理类都是新式类,2:定义成类的属性,,不能定义到构造函数中 #定义一个描述符 class StrNew: def __get__(self, instance, owner): print("Str调用__get__") def __set__(self, instance, value): print("Str调用__set__") def __delete__(self, instance): print("Str调用__delete__") class IntNew: def __get__(self, instance, owner): print("Int调用__get__") def __set__(self, instance, value): print("Int调用__set__") def __delete__(self, instance): print("Int调用__delete__") class People: name=StrNew() age=IntNew() def __init__(self,name,age): self.name=name self.age=age p=People('张三',15) p.name p.name="张三New" del p.name p.age p.age=15 del p.age # 优先级 # 1:类属性>2:数据描述符>3:实例属性>4:非数据描述符>5:找不到属性触发__getattr__() # print("1:类属性>2:数据描述符") # People.name #调用类属性name 本质是调用描述符__get__()方法 # People.name="zhangsan" #它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的 # del People.name print("2:数据描述符>3:实例属性") p=People('张三',15) p.name="lisi" print(p.__dict__) #{} # 与实例相关的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,操作都是跟描述符有关,与实例无关了
# python 是弱类型,可以通过描述将其设置为强类型 # 定义一个类型控制的描述符 class TypeControl: # 定义一个构造函数,用于初始化,字段名称(根据名称对其进行设置)和类型(设置值是做判断) def __init__(self,name,typeName): self.name=name self.typeName = typeName def __get__(self, instance, owner): print("调用__get__") return instance.__dict__[self.name] def __set__(self, instance, value): print("调用__set__") # //如果设置值的属性和定义的属性不一致,报错 if not isinstance(value,self.typeName): raise TypeError instance.__dict__[self.name]=value def __delete__(self, instance): print("调用__delete__") instance.__dict__.pop(self.name) class People: name=TypeControl("name",str) age=TypeControl("age",int) def __init__(self,name,age): self.name=name self.age=age p=People('张三',15) p.name del p.name p=People('1',1) p.name del p.name
21 装饰器
# 装饰器=高阶函数+函数嵌套+闭包 # 高阶函数:传入参数或输出结果是一个函数 # 函数嵌套:函数中定义函数 import time # 添加一个参数,如果参数是n就打n折 def disCount(n=1): def timmer(func): def wrapper(*args,**kwargs): startTime= time.time() res=func(*args,**kwargs)*n; endTime= time.time() print("今天是国庆节,每位客户打的折扣为:"+str(n*10)) return res return wrapper return timmer #@timmer #语法糖,相当于#test=timmer(test) @disCount(n=0.9) def test(a,b): return a+b print(test(100,200))
# 定义一个用户列表 userList=[{"name":"zhangsan","pwd":"333"},{"name":"lisi","pwd":"444"},{"name":"wangwu","pwd":"555"}] currentUser={"name":"None","loginState":"False"} def SignOut(): currentUser = {"name": "None", "loginState": "False"} def authority(func): def wapper(*args,**kwargs): if currentUser["name"] and eval(currentUser["loginState"]): res = func(*args,**kwargs) return res else: SignIn(); return wapper def index(): print("欢迎来到登录页面") @authority def home(): print("欢迎回家%s" %currentUser["name"]) @authority def shopping_car(): print("%s的购物车中有:苹果、葡萄。" %currentUser["name"]) def SignIn(): print("请先登录") userName = input('用户名:').strip() userPwd = input('密码:').strip() for userInfo in userList: if userInfo["name"] == userName and userInfo["pwd"] == userPwd: currentUser["name"] = userName currentUser["loginState"] = "True" if currentUser["name"] and eval(currentUser["loginState"]): print("登录成功,") else: print("用户名或密码错误") SignIn() index() home() shopping_car()
class TypeControl: # 定义一个构造函数,用于初始化,字段名称(根据名称对其进行设置)和类型(设置值是做判断) def __init__(self,name,typeName): self.name=name self.typeName = typeName def __get__(self, instance, owner): print("调用__get__") return instance.__dict__[self.name] def __set__(self, instance, value): print("调用__set__") # //如果设置值的属性和定义的属性不一致,报错 if not isinstance(value,self.typeName): raise TypeError instance.__dict__[self.name]=value def __delete__(self, instance): print("调用__delete__") instance.__dict__.pop(self.name) def typeAssert(**kwargs): def decorateClass(cls): for name,expected_type in kwargs.items(): setattr(cls,name,TypeControl(name,expected_type)) #People.name = TypeControl('name',str) return cls return decorateClass #传递字典参数,运行typeAssert(),返回decorateClass # 继续执行@decorateClass # 执行@decorateClass给类添加属性 @typeAssert(name=str,age=int) class People: def __init__(self,name,age): self.name=name self.age=age p=People('张三',15) p.name del p.name
22 自定义Property属性
class myProperty: def __init__(self,func): self.func=func def __get__(self, instance, owner): # 描述符中调用func函数 # 直接用类调用会由于instance是None,导致运行func()报错 if instance is None: return self return self.func(instance) class Room: def __init__(self,name,length,width): self.name=name self.length=length self.width = width @myProperty #getArea=myProperty(getArea) def getArea(self): return self.width*self.length r1=Room("卧室",6,4) print(r1.getArea) print(Room.getArea) # 思路@myProperty相当于getArea=myProperty(getArea) #1:定义一个getArea=myProperty(getArea) #2:其中定义一个构造函数,接收传递过来的函数名并返回,相当于类的装饰器功能 #3:要想通过.方法名掉用方法。需要通过描述符(3:实例属性>4:非数据描述符)
class myProperty: def __init__(self,func): self.func=func def __get__(self, instance, owner): print("get") # 描述符中调用func函数 # 直接用类调用会由于instance是None,导致运行func()报错 if instance is None: return self res = self.func(instance) setattr(instance,self.func.__name__,res) return res class Room: def __init__(self,name,length,width): self.name=name self.length=length self.width = width @myProperty #getArea=myProperty(getArea) def getArea(self): return self.width*self.length r1=Room("卧室",6,4) print(r1.getArea) print(Room.getArea) print("=======>懒加载,只调用一次,原理:把getArea添加到r1的数据字典中,然后直接从字典中获取") print(r1.getArea) print(r1.getArea) print(r1.getArea) # 思路@myProperty相当于getArea=myProperty(getArea) #1:定义一个getArea=myProperty(getArea) #2:其中定义一个构造函数,接收传递过来的函数名并返回,相当于类的装饰器功能 #3:要想通过.方法名掉用方法。需要通过描述符(3:实例属性>4:非数据描述符)
23 元类
# 我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type # print(type(Foo)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的Foo,即默认的元类为type # # class关键字在帮我们创建类时,必然帮我们调用了元类OldboyTeacher=type(...),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分,分别是 # 1、类名class_name='Foo' # 2、基类们class_bases=(object,) # 3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的 # 调用type时会依次传入以上三个参数 class MyType(type): def __init__(self): print("") def __call__(self, *args, **kwargs): # 此时self 是Foo, #object.__new__(self) 相当于以Foo为类创建对象,即:产生f1 obj=object.__new__(self) #调用Foo下面的init方法 self.__init__(self,*args, **kwargs) class Foo(metaclass=MyType): def __init__(self,name): self.name=name
24 总结
- 面向对象的过程
- 1 开辟一个空间__new__(),属于对象的
- 2 把对象的空间传递给self,执行__init__()方法
- 3 把对象空间返还给调用者
- 内置方法、双下方法、魔术方法
- 1 格式:__方法名__
- 内置函数和类的内置方法之间联系紧密
- 常见的内置方法有 __new__ __init__ __call__ __str__...
- set去重原理
- set() 函数中会先调用对象的 __hash__() 方法,获取 hash 结果;
- 如果 hash 结果相同,用比较操作符 == (也就是调用函数 __eq__())判断二者的值是否相等;
- 如果都相等,去重;否则,set() 认为二者不同,两个都保留到结果中。