饮冰三年-人工智能-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>}

ViewCode

  类是什么时候被加载的,以及类名是什么时候生效的?

    创建一个类的空间,然后从上到下加载内容(属性、方法)。然后把这个命名空间指向类名。

  饮冰三年-人工智能-Python-18Python面向对象

  为什么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','','',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","","2号线")
sub.run() cr=Car("汽车","80km/h","","红旗")
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) #
'''虽然以_开头但是还可以被访问到,因为这只是一种约定'''
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) #
'''虽然以__开头不可以被直接访问到,但可以通过“_+类名+自定义名称'''
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,'',"该属性不存在"))
print(getattr(a1,'',"该方法不存在"))
# 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)

反射2

# 导入其他模块进行反射
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("")
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("")

9 __new__

 class Single:
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls) #类名() 调用了new方法,开辟可一个空间给obj。
print("init中的self:", obj)
return obj
def __init__(self):
print("init中的self:",self)
sin=Single() # 输出结果,
# init中的self: <__main__.Single object at 0x00CDA930>
# init中的self: <__main__.Single object at 0x00CDA930> # 结论:line 8 :类名() 调用了new方法,开辟可一个空间给obj。
# 然后把obj的空间给self。#
# line 6 :执行__init__ 方法
# 最后把空间给调用者sin

__new__

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__方法

#__getattribute__与__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"]=""
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__来代替

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

__len__

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方法

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) #
#
# ====>
#
#
#
#
#
#
#
#
#

斐波那契数列

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("")
print(aa)
print(exc_type)
print(exc_val)
print(exc_tb)
print("")
return True
with OpenNew("a.txt") as f:
print('')
print('')
print('')

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)
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":""},{"name":"lisi","pwd":""},{"name":"wangwu","pwd":""}]
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:非数据描述符)

自定义属性myProperty

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:非数据描述符)

懒加载myProperty

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() 认为二者不同,两个都保留到结果中。
上一篇:Android -- Options Menu,Context Menu,Popup Menu


下一篇:JS限制并且显示textarea字数