一、类和对象概述

三大编程范式(面向过程编程、函数式编程、面向对象编程)

编程范式就是编程的方法论,表示一种编程风格,不同风格没有高低之分

编程进化论:非结构化程序不断地结构化(函数区分功能,面向对象把功能和数据整合到一起)

面向对象设计例子(不一定要用class来写,def也可以写):

def dog(name, gender, type):
def jiao(dog):
print("一条狗[%s], barking" %dog["name"])
def play(dog):
print("一条[%s]playing" %dog["type"])
def init(name, gender, type):
dog1 = {"name": name,
"gender": gender,
"type": type,
"jiao": jiao,
"play": play,
}
return dog1
return init(name, gender, type)

d1 = dog('aaa', 'male', 'za')
d2 = dog('bbb', 'female', 'jb')
print(d1)
print(d2)
d1['jiao'](d1)
d2['play'](d2)

类:把一类事物的相同特征和动作(数据属性和函数属性)整合到一起就是类,类是一个抽象的概念(只是一个模板、总结归纳)

对象:就是基于类而创建的一个具体的事物(具体存在的),也是特征和动作整合到一起

面向对象设计:把数据属性和函数属性整合到一起形成类,就是面向对象设计,通过类实例化出对象,就叫实例化的过程

面向对象编程:固定格式,用class定义类的方式去写面向对象设计的程序

 二、类相关知识

 在python中声明函数和声明类很相似

 声明类名时,规范是类名首字母要大写

 类名加()表示实例化出一个对象(对象=实例),类的返回值就是一个具体的实例,本质也是一个return,函数加()表示运行一段逻辑

class Chinese:
'this is a class of Chinese'
pass
print(Chinese)#结果是<class '__main__.Chinese'>,右键执行文件时,文件名变为__main__

p1 = Chinese()#实例化
print(p1)

经典类与新式类:python2中区分经典类和新式类

经典类特点:类名后直接是冒号

新式类特点:类名后是(object)

python3中统一都是新式类

 

类是用来描述一类事物,类的对象指的是这一类事物中的一个个体

是事物就要有属性,属性分为:

1.数据属性:就是变量

2.函数属性:就是函数,在面向对象里通常称为方法

类和对象均用点来访问自己的属性

类生产对象的过程叫做实例化,类实例化的结果就是一个对象,或者叫做一个实例 (对象=实例)

class Chinese:
'这是一个中国人的类'
party = 'cp'
def spit():
print('正在spit')
def cut_in_line(self):
print('走到了前面')

print(Chinese.party)#类和对象均用点来访问自己的属性,本质上是到自己的属性字典里找东西
Chinese.spit()

print(dir(Chinese))#查看函数属性,输出是一个列表,里面是属性名
print(Chinese.__dict__)#查看类的属性字典,key为属性名,value为属性值
print(Chinese.__dict__['party'])
Chinese.__dict__['spit']()

类的其他属性:

class Chinese:
'这是一个中国人的类'
party = 'cp'
def spit():
print('正在spit')
def cut_in_line(self):
print('走到了前面')

print(Chinese.__name__)#显示类名
print(Chinese.__doc__)#显示类的文档
print(Chinese.__base__)#python中所有的类都有一个共同的祖先:object
print(Chinese.__bases__)#以元组的形式显示类的祖先
print(Chinese.__module__)#显示类所在模块
print(Chinese.__class__)#实例对应的类(仅新式类中)

三、对象相关知识

class Chinese:
'这是一个中国人的类'
party = 'cp'
def __init__(name, age, gender): #类会自动检索到这个初始化函数,会自动将类接受的参数传给初始化参数,
初始化函数逻辑加载一遍后会自动return 一个字典,不需要用return __init__()来接收它return的值
dic = {'name': name, 'age': age, 'gender': gender}
return dic
def spit():
print('正在spit')
def cut_in_line(self):
print('走到了前面')

p1 = Chinese('cyx', 13, 'male')#实例化,参数会自动传给__init__函数
class Chinese:
'这是一个中国人的类'
party = 'cp'
def __init__(self, name, age, gender):#类当中必须有一个初始化的函数来定制每一个对象的属性,函数必须有一个self参数,其次再写数据属性
print('start running')
self.mingzi = name #self是实例本身,就是p1,self.mingzi代表给self赋予了一个mingzi属性
self.nianji = age #p1.nianji = age
self.xingbie = gender
print('end running')
def spit():
print('正在spit')
def cut_in_line(self):
print('%s 走到了前面' %self.mingzi)

p1 = Chinese('cyx', 13, 'male')#实例化,参数会自动传给__init__函数,实例化本质上就是调用了init函数的运行,p1传给了self做参数
print(p1.__dict__)#输出p1的字典形式,实例只有数据属性,没有函数属性,因为实例是执行__init__方法生成的一个字典,里面并不包含函数属性
print(p1.__dict__['mingzi'])#同下一行代码
print(p1.xingbie)
print(p1.party)#实例现在__init__的作用域里找key,没找到,再到外层Chinese中找到party,如果类中找不到就会报错,所以实例可以调到类的数据属性,跟函数的作用域一样

Chinese.cut_in_line(p1)

# p1.spit()#class会默认将p1传给 spit(),spit()不需要参数,所以会报错
p1.cut_in_line()#class会默认将p1传给cut_in_line()

实例只有数据属性,函数属性找类要,这么做的好处是:实例通过引用的方式去调类的属性,这样可以省内存

class Chinese:
'这是一个中国人的类'
party = 'cp'
def __init__(self, name, age, gender):
self.mingzi = name
self.nianji = age
self.xingbie = gender
def spit(self):
print('%s正在spit' %self.mingzi)
def cut_in_line(self):
print('%s 走到了前面' %self.mingzi)
def eat_food(self, food):
print('%s正在吃%s' %(self.mingzi, food))

p1 = Chinese('cyx', 13, 'male')
p1.spit()
p1.eat_food('banana')

p2 = Chinese('jik', 12, 'male')
p2.eat_food('apple')
print(dir(p2))#实例也有dir方法

四、类属性增删改查

凡是类调用的方法,默认都有一个参数self,放在函数的第一个位置,这是函数独有的语法结构要求的,这样在调用每个函数时,类会自动把实例传到第一个位置

class Chinese:
'这是一个中国人的类'
party = 'cp'
def __init__(self, name, age, gender):
self.mingzi = name
self.nianji = age
self.xingbie = gender
def spit(self):
print('%s正在spit' %self.mingzi)
def cut_in_line(self):
print('%s 走到了前面' %self.mingzi)
def eat_food(self, food):
print('%s正在吃%s' %(self.mingzi, food))

#查看类属性
print(Chinese.party)

#修改类属性
#改数据属性
Chinese.party = 'CP'
print(Chinese.party)
p1 = Chinese('yjj', 23, 'girl')
print(p1.party)
#改函数属性
def test(self):
print('test')
Chinese.play_ball = test
p1.play_ball()#类的函数属性修改会立马体现给实例,因为实例是通过调用类的函数

#增加类属性
#加数据属性
Chinese.country = 'China'
print(Chinese.country)
print(p1.country)
#加函数属性
def play_ball(self, ball):
print('%s在打%s' %(self.mingzi, ball))
Chinese.play = play_ball
p1.play('football')

#删除类属性
del Chinese.party
del Chinese.country
print(Chinese.__dict__)

五、实例属性增删改查

class Chinese:
'这是一个中国人的类'
party = 'cp'
def __init__(self, name, age, gender):
self.mingzi = name
self.nianji = age
self.xingbie = gender
def eat_food(self, food):
print('%s正在吃%s' % (self.mingzi, food))

#生成实例
p1 = Chinese('ypp', '18', 'boy')
#查看实例属性字典
print(p1.__dict__)
#查看实例属性
print(p1.mingzi)
print(p1.eat_food)
#增加
p1.hobby = 'reading'
print(p1.__dict__)
print(p1.hobby)

def test(self):
print('实例的函数属性')
p1.test = test
print(p1.__dict__)
print(p1.test)
# p1.test()#报错,这里p1调用的是实例的类,class只有在实例调用类的方法时,才会自动传self
p1.test(p1)#上一行代码修改成此行即可运行

# #不要修改底层的属性字典,虽然可以实现
# p1.__dict__['gh'] = 'jk'
# print(p1.__dict__)
# print(p1.gh)

#修改
p1.nianji = 45
print(p1.__dict__)
print(p1.nianji)

#删除
del p1.nianji
print(p1.__dict__)

六、类与对象属性注意点

 例1:

class Chinese:
country='China'
def __init__(self, name):
self.name = name

def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese('alex')
print(p1.country)#访问类的属性
p1.country = 'Japan'#在p1字典里新增,跟类字典没有关系
print(Chinese.country)#China
print(p1.country)#Japan

例2:

country = 'China'
class Chinese:

def __init__(self, name):
self.name = name

def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese('alex')
print(p1.country)#报错,p1只在类里面找,找不到不会去外面找

例3:init只特殊在self,不能return值,其余跟函数没有区别

country = 'China'
class Chinese:
def __init__(self):
name = input('请输入用户名:')
self.name = name

def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))
p1=Chinese()
print(p1.__dict__)

此例属于输入和函数耦合到一起,不符合规范,修改如下

country = 'China'
class Chinese:
def __init__(self, name):
self.name = name

def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))

name = input('>>>:')
p1=Chinese(name)
print(p1.__dict__)

例4:

country = 'China'
class Chinese:
def __init__(self, name):
self.name = name
print(country)#没加点表明其既不是类的属性也不是实例的属性,只是一个普通变量,可以到类外面找到全局变量

def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))

p1=Chinese('cyx')

例5:

country = 'China2'
class Chinese:
country = 'China1'#只有类或实例加.可以调用到,country = 'China1'存在于类的字典里,普通变量country不会跑到类或实例的字典里去找
def __init__(self, name):
self.name = name
print(country)#调不到类的country

def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))

p1=Chinese('cyx')

例6:

class Chinese:
country = 'China'#只有类或实例加.可以调用到,country = 'China1'存在于类的字典里,普通变量country不会跑到类或实例的字典里去找
l = ['a', 'b']
def __init__(self, name):
self.name = name

def play_ball(self, ball):
print('%s 正在打 %s' %(self.name, ball))

p1=Chinese('cyx')
print(p1.l)
# p1.l = [1, 2, 3]#给p1新增一个属性
# print(p1.__dict__)
# print(Chinese.l)
p1.l.append('c')#没有给p1新定义属性,l.append操作的是类的属性
print(p1.__dict__)
print(Chinese.l)

七、静态属性

class Room:
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.width = width
self.length = length
self.height = height
@property #可以封装逻辑,自己定制一段逻辑,让用户调用的时候完全感知不到后端执行的是什么样的逻辑,就像是在调用普通的数据属性一样
def cal_area(self):
return self.width * self.length

r1 = Room('bedroom', 'tom', 100, 100, 10000)
r2 = Room('baseroom', 'jerry', 10, 10, 100)
print(r1.cal_area)
print(r2.cal_area)#让函数属性看起来像数据属性,调用方式不需要加()

 八、类方法:不通过任何实例,只执行类的方法,把类和实例完全区分开

用处:跟实例没有任何关系,只是类级别的操作的时候会用

class Room:
tag = 1
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.width = width
self.length = length
self.height = height
@property
def cal_area(self):
return self.width * self.length
def test(self):
print('from test', self.name)

@classmethod#专门给类用的
def tell_info(cls, x):
print(cls)
print(cls.tag, x)

Room.tell_info(10)#会自动传入第一个参数Room

九、静态方法:只是名义上归属类管理,不能使用类变量和实例变量,是类的工具包

class Room:
tag = 1
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.width = width
self.length = length
self.height = height
@property
def cal_area(self):
return self.width * self.length

def test(self):
print('from test', self.name)

@classmethod#专门给类用的
def tell_info(cls, x):
print(cls)
print(cls.tag, x)

@staticmethod
def play_ball(a, b, c):
print('%s %s %s正在踢球' %(a, b, c))#类和实例调用都没有问题

def test2(x, y):
print(x, y)#毫无意义,这不是静态方法,用类调用没问题,用实例调用会出问题 ,因为实例会把自己传入第一个参数,无人接收

Room.play_ball('tom', 'jerry', 'jack')

r1 = Room('bedroom', 'tom', 100, 100, 10000)
r1.play_ball('tom', 'jerry', 'jack')

Room.test2(1, 2)
r1.test2(1, 2)#会报错

十、组合:关联类和类(类和类之间没有共同点,但是有关联,可以用组合实现关联)

class School:
def __init__(self, name, addr):
self.name = name
self.addr = addr

def enroll_stu(self):
print('%s 正在招生' %self.name)

class Course:
def __init__(self, name, price, period, school):
self.name = name
self.price = price
self.period = period
self.school = school

s1=School('xdf','北京')
s2=School('xdf','南京')
s3=School('xdf','天津')

msg = '''
1新东方 北京校区
2新东方 南京校区
3新东方 天津校区
'''

while True:
print(msg)
menu = {
'1': s1,
'2': s2,
'3': s3,
}
choice = input('请选择学校:')
sch_obj = menu[choice]
name_choice = input('课程名:')
price_choice = input('课程费用:')
period_choice = input('课程周期:')
new_course = Course(name_choice, price_choice, period_choice, sch_obj)
print("课程%s属于%s学校" %(new_course.name, new_course.school.name))

十一、面向对象编程三大特性:继承、多态、封装

继承

继承分单继承(括号里有一个父类)和多继承(括号里有多个父类)

class SubClass(ParentClass1, ParentClass2):

子类继承了父类的所有属性,子类自定义的属性如果跟父类重名,等于子类在自己的字典里新建了一个属性,找的时候现在自己这里找

class Dad:
'这个是父类'
money = 10
def __init__(self, name):
print('father')
self.name = name
def hit_son(self):
print('%s正在打儿子' %self.name)

class Son(Dad):
money = 1000000


s1 = Son('cyx')
print(s1.money)
print(Dad.money)

 

接口继承:代表父类当中规定好了子类必须实现什么方法,但是父类里不去实现,子类只要继承了父类,子类必须在自己的类中去具体的实现父类规定的方法

接口:一个方法,一个具体的函数 

归一化设计:使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合

import abc
class All_file(metaclass=abc.ABCMeta):#父类规定所有子类必须实现的方法,接口类的方法不用实现内部逻辑,只是来规范子类,没有必要实例化

@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')
class Cdrom(All_file):
def read(self):
print('cdrom read')
def write(self):
print('cdrom write')
class Mem(All_file):
def read(self):
print('mem read')

m1 = Mem()#会报错,因为父类当中规定好了子类必须实现什么方法,但是父类里不去实现,子类只要继承了父类,子类必须在自己的类中去具体的实现父类规定的方法

 继承顺序

 

 

 

class A:
def test(self):
print('A')

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 E C A 新式类的继承顺序
F  D  B  A  E  C    经典类的继承顺序

经典类深度优先,新式类广度优先

print(F.__mro__)
#(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

 python2中的经典类和新式类

 

 子类中调用父类的方法:

方法1:可扩展性差,一旦基类名修改 ,子类里都得改

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)
self.line=line

def show_info(self):
print(self.name,self.speed,self.load,self.power,self.line)

def run(self):
Vehicle.run(self)
print('%s %s 线,开动啦' %(self.name,self.line))
line13=Subway('北京地铁','10km/s',1000000000,'电',13)

line13.show_info()

line13.run()

 方法2:不用写父类名,不用传self参数

 

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)#=super(Subway, self).__init__(name, speed, load, power)#不用加self
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()#不用加self
print('%s %s 线,开动啦' %(self.name,self.line))
line13=Subway('北京地铁','10km/s',1000000000,'电',13)

line13.show_info()

line13.run()

 多态:运行时的绑定状态(绑定了同一个状态是因为继承了同一个父类)

由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同

class H2O:
def __init__(self, name, temperature):
self.name = name
self.temperature = temperature
def turn_ice(self):
if self.temperature < 0:
print('【%s】 变成冰' %self.name)
elif self.temperature > 0 and self.temperature < 100:
print('【%s】 变成水')
elif self.temperature > 100:
print('【%s】 变成水蒸气')

class Water(H2O):
pass
class Ice(H2O):
pass
class Steam(H2O):
pass

w1 = Water('水', 5)
i1 = Ice('冰', -6)
s1 = Steam('蒸汽', 134)

def func(obj):
obj.turn_ice()

func(w1)
func(i1)
func(s1)

# w1.turn_ice()
# i1.turn_ice()
# s1.turn_ice()

封装:本质是明确地区分内外

第一个层面的封装:类本身就是在封装数据属性和函数属性

第二个层面的封装:类中定义私有的,只在类内部使用,外部无法访问

约定1:任何以单下划线开头的名字都应该是内部的、私有的

真要调用也可以调用,只是不符合约定

约定2:双下划綫开头的名字

直接__名字调用不了,要用_类名__名字调用才可以

class H2O:
__sdf = "jkl"
def __init__(self, name, temperature):
self.name = name
self.temperature = temperature
def turn_ice(self):
if self.temperature < 0:
print('【%s】 变成冰' %self.name)
elif self.temperature > 0 and self.temperature < 100:
print('【%s】 变成水')
elif self.temperature > 100:
print('【%s】 变成水蒸气')

class Water(H2O):
pass
class Ice(H2O):
pass
class Steam(H2O):
pass

w1 = Water('水', 5)
# print(w1._sdf)
# print(w1.__sdf)
print(w1._H2O__sdf)

第三个层面的封装:真正意义的封装,明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用

class Room:
def __init__(self, name, owner, width, length, height):
self.name = name
self.owner = owner
self.__width = width
self.__length = length
self.__height = height

def tell_area(self):
return self.__width * self.__length

r1 = Room('restroom', 'cyx', 100, 100, 45)
print(r1.tell_area())

自省/反射

指程序可以访问、检测和修改它本身状态或行为的一种能力。

 四个可以实现自省的函数:hasattr  getattr   setattr   delattr

class DishonestMiddleman:
feature = 'ugly'
def __init__(self, name, addr):
self.name = name
self.addr = addr
def sell_house(self):
print('%s正在卖房子' %self.name)
def rent_house(self):
print('%s正在租房子' %self.name)
#hasattr检测d1能否调用到''里的属性
d1 = DishonestMiddleman('dabeitou', 'toilet')
print(hasattr(d1, 'name'))
print(hasattr(d1, 'sell_house'))
print(hasattr(d1, 'sell_hrtyuuouse'))#返回false

#getattr(x, 'y')=x.y
print(getattr(d1, 'name'))#=d1.name
print(getattr(d1, 'sell_house'))
func = getattr(d1, 'sell_house')
func()
# print(getattr(d1, 'selsdfl_house'))#没有则报错
print(getattr(d1, 'sell_houfghhse', 'no such attribute'))#写了默认参数找不到也不后会报错,会返回默认参数

setattr(d1, 'sb', True)#=d1.sb = Truekey值不存在时,新增属性
setattr(d1, 'name', 'SB')#key值存在时,修改属性
setattr(d1, 'func', lambda x:x+1)
setattr(d1, 'func1', lambda self:self.name + 'sb')
print(d1.__dict__)
print(d1.func(10))
print(d1.func1(d1))

#delattr(x, 'y') = del x.y
delattr(d1, 'name')
print(d1.__dict__)

动态导入模块:__import__('文件名 ')

from m1 import t#m1/t

#如何以字符串形式导入模块
module = __import__('m1.t')#导入时会将t.py运行一遍,不管套多少层,最后返回的是最顶层的模块
print(module)#__import__通过字符串的形式导入模块,导入是最顶级的模块
module.t.test1()
# from m1.t import *
#
# test1()
# test2()#如果在模块属性前加_,就不能在import*时被导入,但是Python没有真正意义上的限制,解决方法如下

from m1.t import test1, _test2

test1()
_test2()
import importlib
m = importlib.import_module('m1.t')
print(m)#得到的结果是m1.t模块,不同于__import__
class DishonestMiddleman:
feature = 'ugly'
def __init__(self, name, addr):
self.name = name
self.addr = addr
def sell_house(self):
print('%s正在卖房子' %self.name)
def rent_house(self):
print('%s正在租房子' %self.name)

print(hasattr(DishonestMiddleman, 'feature'))#类本质上也是对象
class Foo:
x = 1
def __init__(self, y):
self.y = y

def __getattr__(self, item):
print('执行__getattr__')

#调用对象不存在的属性时,__getattr__才会触发运行
f = Foo(10)
f.ggggg
class Foo:
x = 1
def __init__(self, y):
self.y = y

def __delattr__(self, item):
print('执行__delattr__')

f = Foo(10)
#删除时会触发__delattr__
del f.y
del f.x
class Foo:
x = 1
def __init__(self, y):
self.y = y

def __setattr__(self, key, value):
print('执行__setattr__')
# self.key = value会进入无限递归,因为一执行就会触发__setattr__
self.__dict__[key] = value
f = Foo(10)
f.z = 3
print(f.__dict__)

 

class Foo:
pass

f1 = Foo()
# print(f1.x)#只有属性不存在时,会自动触发__getattr__,直接报错,若有自定义__getattr__,则使用自定义的__getattr__
# del f1.x#删除属性时会触发__delattr__
f1.x = 10#设置属性时会触发__setattr__
class Foo:
def __init__(self, name):
self.name = name

def __getattr__(self, item):
print('你找的属性【%s】不存在' %item)


f1 = Foo('cyx')
print(f1.age)
需求:添加的属性必须是字符串类型
class Foo:
def __init__(self, name):
self.name = name

def __getattr__(self, item):
print('你找的属性【%s】不存在' %item)

def __setattr__(self, key, value):
print('执行setattr', key, value)
if type(value) is str:
print('开始设置')
# self.k = v#会触发__setattr__,继而进入死循环
self.__dict__[key] = value.upper()#可以定制设置属性的过程
else:
print('必须是字符串类型')

f1 = Foo('cyx')#触发self.name = name,进而触发__setatr__
f1.age = 18#触发__setattr__
f1.gender = 'male'
print(f1.__dict__)

需求:不允许删除属性

class Foo:
def __init__(self, name):
self.name = name

def __getattr__(self, item):
print('你找的属性【%s】不存在' %item)

def __setattr__(self, key, value):
print('执行setattr', key, value)
if type(value) is str:
print('开始设置')
# self.k = v#会触发__setattr__,继而进入死循环
self.__dict__[key] = value.upper()#可以定制设置属性的过程
else:
print('必须是字符串类型')

def __delattr__(self, item):
print('不允许删除属性【%s】' %item)
# # del self.item#触发__delattr__,继而进入死循环
# self.__dict__.pop(item)

f1 = Foo('cyx')#触发self.name = name,进而触发__setatr__
print(f1.__dict__)
del f1.name
print(f1.__dict__)

包装标准类型

class List(list):
def append(self, p_object):
if type(p_object) is str:
# self.append(p_object)会陷入无限递归
list.append(self, p_object)#推荐用super().append(p_object)
else:
print('必须是字符串类型')
def show_middle(self):
middle_index = int(len(self)/2)
return self[middle_index]

l1 = List('helloworld')
print(l1, type(l1))
print(l1.show_middle())
l1.append('cyx')
print(l1)
l1.append(1765)

 isinstance 和issubclass

class Foo:
pass

class Bar(Foo):
pass

f1 = Foo()
print(isinstance(f1, Foo))
print(issubclass(Bar, Foo))

__getattribute__

class Foo:
def __init__(self, x):
self.x = x

def __getattr__(self, item):
print('执行__getattr__')

def __getattribute__(self, item):#不管属性找不找的到,都会触发getattribute
print('执行__getattribute__')
raise AttributeError('抛出异常了')#__getattribute__抛出AttributeError异常时才会触发__getattr_运行


f1 = Foo(10)
f1.x
f1.xxxxxx

__setitem__  __getitem__  __delitem__:[]的方式操作才会触发,.的方式操作不会触发

class Foo:
def __getitem__(self, item):
print('getitem')
def __setitem__(self, key, value):
print('setitem')
def __delitem__(self, key):
print('delitem')

f1 = Foo()
print(f1.__dict__)
f1['name'] = 'cyx'
f1['age'] = 18
print(f1.__dict__)#因为setitem里没有定制属性的逻辑,所以输出是{}
class Foo:
def __getitem__(self, item):
print('getitem')
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key] = value

def __delitem__(self, key):
print('delitem')
self.__dict__.pop(key)

f1 = Foo()
print(f1.__dict__)
f1['name'] = 'cyx'
f1['age'] = 18
print(f1.__dict__)#因为setitem有定制属性的逻辑,所以输出是{'name': 'cyx', 'age': 18}

del f1.name#会触发__delattr__,不会触发__delitem__
print(f1.__dict__)

print(f1.age)#会触发__getattr__,不会触发__getitem__
class Foo:
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 = Foo()
print(f1.__dict__)
f1['name'] = 'cyx'
f1['age'] = 18
print(f1.__dict__)#因为setitem有定制属性的逻辑,所以输出是{'name': 'cyx', 'age': 18}

del f1['name']
print(f1.__dict__)

print(f1['age'])
改变对象的字符串显示:__str__   __repr__
class Foo:
pass
f1 = Foo()
print(f1)#print触发系统内的__str__(f1)→f1.__str__(),输出<__main__.Foo object at 0x0000000001E7A9E8>
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('cyx', 18)#不再输出系统默认的格式,而是输出类中定制的格式名字是cyx 年龄是18
print(f1)

x = str(f1)
print(x)#相当于print(f1)

y = f1.__str__()
print(y)#相当于print(f1)
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('cyx', 18)
print(f1)#print找的顺序str(f1) f1.__str__() repr(f1) f1.__repr__()
总结:str函数或者print函数→obj.__str__()
repr或者交互式解释器→obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__
注意:这两个方法的返回值必须是字符串 ,否则抛出异常
__format__
x = '{0}{0}{0}'.format('dog')
print(x)#dogdogdog

class Date:
def __init__(self, year, mon, day):
self.year = year
self.mon = mon
self.day = day

d1 = Date(2018, 5, 20)

x = '{0.year}{0.mon}{0.day}'.format(d1)
y = '{0.year}:{0.mon}:{0.day}'.format(d1)
z = '{0.mon}-{0.day}-{0.year}'.format(d1)
print(x)
print(y)
print(z)
format_dic = {
'ymd': '{0.year}{0.mon}{0.day}',
'y:m:d': '{0.year}:{0.mon}:{0.day}',
'm-d-y': '{0.mon}:{0.day}:{0.year}'
}
class Date:
def __init__(self, year, mon, day):
self.year = year
self.mon = mon
self.day = day
def __format__(self, format_spec):
if not format_spec or format_spec not in format_dic:
format_spec = 'ymd'
fm = format_dic[format_spec]
return fm.format(self)

d1 = Date(2018, 5, 20)
print(d1.__format__('ymd'))
print(d1.__format__('y:m:d'))
print(d1.__format__('m-d-y'))
print(d1.__format__(''))
print(d1.__format__('hkhg'))

print(format(d1, ''))
print(format(d1))
__slots:定义在类中的类变量,用来省内存,实例只能定义slots提供的属性
class Foo:
__slots__ = 'name'#{''name': None},这个类产生的实例不再具有__dict__属性,限制了实例的属性创建

f1 = Foo()
f1.name = 'cyx'
print(f1.name)

# print(f1.__dict__)
# f1.age = 18本质是触发__setattr__,进一步触发f1.__dict__['age'] = 18
print(Foo.__slots__)
print(f1.__slots__)
__doc__:查看描述信息
class Foo:
'描述信息'
pass

class Bar(Foo):
pass

print(Foo.__dict__)
print(Bar.__dict__)

print(Foo.__doc__)
print(Bar.__doc__)#__doc__属性无法被继承
__module__和__class__查看实例来自哪个模块和来自哪个类

析构方法__del__
class Foo:
def __init__(self, name):
self.name = name
def __del__(self):
print('执行__del__')

f1 = Foo('cyx')
del f1
print('---->')

class Foo:
def __init__(self, name):
self.name = name
def __del__(self):
print('执行__del__')

f1 = Foo('cyx')
print('---->')#文件执行完后,内存释放,会触发__del__

class Foo:
def __init__(self, name):
self.name = name
def __del__(self):
print('执行__del__')

f1 = Foo('cyx')
del f1.name#del在实例被删除时才会触发,删除属性时不会触发
print('---->')#文件执行完后,内存释放,会触发__del__
__call__对象后面加括号,触发执行,调用对象类下的__call__方法
class Foo:
def __call__(self, *args, **kwargs):
print('执行__call__')

f1 = Foo()
f1()#执行f1类下的__call__方法
Foo()#执行Foo类下的__call__方法,返回一个实例
__next__和__iter__实现迭代器协议

class Foo:
def __init__(self, n):
self.n = n
def __iter__(self):#__iter__将对象变成一个可迭代的对象(在类中必须有一个__iter__方法,才能将对象变成一个可迭代对象)
return self
def __next__(self):
if self.n == 13:
raise StopIteration('终止了')
self.n += 1
return self.n

f1 = Foo(10)
print(f1.__next__())
print(next(f1))
print(next(f1))
print(next(f1))#会报错
class Foo:
def __init__(self, n):
self.n = n
def __iter__(self):#__iter__将对象变成一个可迭代的对象(在类中必须有一个__iter__方法,才能将对象变成一个可迭代对象)
return self
def __next__(self):
if self.n == 13:
raise StopIteration('终止了')
self.n += 1
return self.n

f1 = Foo(10)
for i in f1:#iter(f1)→f1.__iter__()
print(i)#不会报错
迭代器协议实现斐波那契数列
class Fib:
def __init__(self):
self._a = 1
self._b = 1

def __iter__(self):
return self

def __next__(self):
if self._a > 100:
raise StopIteration('终止了')
self._a, self._b = self._b, self._a + self._b
return self._a

f1 = Fib()
print(next(f1))
print(next(f1))
print(next(f1))
print(next(f1))
print('------>')
for i in f1:#for会接着当前位置继续往下走,不会从头来过
print(i)
描述符理论
描述符定义:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete()中的一个,这也被称为描述器协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete():采用del删除属性时,触发
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 = 'cyx'
print(f1.name)
del f1.name#描述符产生的实例进行属性操作并不会触发三个方法的执行
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()

print(Bar.__dict__)
b1 = Bar()
print(b1.x)#另外一个类产生的实例调用时,触发描述符Foo下的__get__方法
b1.x = 1#触发描述符Foo下的__set__方法
print(b1.__dict__)
del b1.x#触发描述符Foo下的__delete__方法
描述符分类:数据描述符和非数据描述符
数据描述符:至少实现了__get__(),__set__()

非数据描述符:没有实现__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()#一旦定义,后续对x属性的调用全都去找Foo

def __init__(self, n):
self.x = n


b1 = Bar(10)#self.x = 10,触发set方法
print(b1.__dict__)
注意事项:
描述符本身应该定义成新式类,被代理的类也应该是新式类
必须把描述符定义成被代理类的类属性,不能为定义到构造函数中
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
instance.__dict__['x'] = value
def __delete__(self, instance):
print('__delete__方法')

class Bar:
x = Foo()
def __init__(self, n):
self.x = n


b1 = Bar(10)#self.x = 10,触发set方法
print(b1.__dict__)
b1.x = 111111
print(b1.__dict__)
b1.y = 'hji'
print(b1.__dict__)#没有被代理的属性就按正常操作即可
描述符优先级:
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

类属性>数据描述符
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
def __delete__(self, instance):
print('__delete__方法')

class Bar:
x = Foo()

print(Bar.x)#属性字典里找x,x被代理,找Foo,触发__get__
Bar.x =1 #类属性优先级>描述符,重新定义赋值,和描述符没关系了,实质是底层字典的覆盖/修改操作
print(Bar.__dict__)
print(Bar.x)
数据描述符>实例属性
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
def __delete__(self, instance):
print('__delete__方法')

class Bar:
x = Foo()

print(Bar.__dict__)
b1 = Bar()
b1.x#先在b1的字典里找x,找不到再去类的字典里找,类x对应的是描述符对象,触发__get__方法
b1.x =1 #先在b1的字典里找x,找不到再去类的字典里找,类x对应的是描述符对象,触发__set__方法
print(b1.__dict__)
del b1.x#先在b1的字典里找x,找不到再去类的字典里找,类x对应的是描述符对象,触发__delete__方法
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
print('__set__方法', instance, value)
def __delete__(self, instance):
print('__delete__方法')

class Bar:
x = Foo()

b1 = Bar()
Bar.x = 187655#描述符被覆盖
b1.x#不再调用描述符

del Bar.x
b1.x
实例属性>非数据描述符
class Foo:
def __get__(self, instance, owner):
print('__get__方法')

class Bar:
x = Foo()

b1 = Bar()
# b1.x#先在b1的字典里找x,找不到再去类的字典里找,类x对应的是描述符对象,触发__get__方法
b1.x =1
print(b1.__dict__)
数据描述符>实例属性
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
pass

class Bar:
x = Foo()

b1 = Bar()
# b1.x#先在b1的字典里找x,找不到再去类的字典里找,类x对应的是描述符对象,触发__get__方法
b1.x =1
print(b1.__dict__)
找不到触发__getattr__
class Foo:
def __get__(self, instance, owner):
print('__get__方法')
def __set__(self, instance, value):
pass

class Bar:
x = Foo()
def __getattr__(self, item):
print('------>')

b1 = Bar()
b1.x =1
print(b1.__dict__)
b1.xxxxxxxxxxxxx

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

#__getattr__,__setattr__,__delattr__
obj点的方式去操作属性时触发的方法

__getattr__:obj.属性 不存在时触发
__setattr__:obj.属性=属性的值 时触发
__delattr__:del obj.属性 时触发

#__getitem__,__setitem_,__delitem__
obj[‘属性’]的方式去操作属性时触发的方法

__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发

#__get__,__set__,__delete__
描述就是一个新式类,这个类至少要实现上述三个方法的一个
class 描述符:
def __get__():
pass
def __set__():
pass
def __delete__():
pass

class 类:
name=描述符()

obj=类()
obj.name #get
obj.name='egon' #set
del obj.name #delete

#__del__:析构方法
垃圾回收时触发

上下文管理协议

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)

with Foo('a.txt') as f:#with Foo('a.txt')触发__enter__,__enter__返回的值赋值给f
print(f)
print(f.name)#无异常时代码块执行完毕时触发__exit__
print('5555')
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)#异常traceback

with Foo('a.txt') as f:#with Foo('a.txt')触发__enter__,__enter__返回的值赋值给f
print(f)
print(ahdkhjjv)#有异常时直接执行__exit__,后面代码不再执行
print(f.name)
print('5555')
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)#异常traceback
return True#将异常吸收掉了,with正常结束掉
with Foo('a.txt') as f:#with Foo('a.txt')触发__enter__,__enter__返回的值赋值给f
print(f)
print(ahdkhjjv)#有异常时直接执行__exit__,后面代码不再执行,除非在__exit__中return True
print(f.name)
print('1111')
print('5555')

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语句的执行完毕

描述符应用

用描述符为Python加上类型检测

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)
if not isinstance(value, self.expected_type):
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)
age = Typed('age', int)
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary

p1 = People('cyx', '12', 15.9)
print(p1.__dict__)

# p1.name
# p1.name = 'ypp'
# print(p1.__dict__)

类的装饰器的基本原理

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

@Typed(x=1, y=2, z=3)
class Foo:
pass
print(Foo.__dict__)

@Typed(name = 'cyx')
class Bar:
pass
print(Bar.__dict__)

 通过装饰器给类加上描述符

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)
if not isinstance(value, self.expected_type):
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):
def wrapper(obj):
for key, val in kwargs.items():
setattr(obj, key, Typed(key, val))
return obj
return wrapper

@deco(name = str, age = int, salary = float)
class People:
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary

p1 = People('cyx', 12, 15.9)
print(People.__dict__)

 利用描述符自定制property&实现延迟计算功能

class Lazyproperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):#类调用时,传入的instance是None
print('get')
if instance is None:
return self
res = self.func(instance)
setattr(instance, self.func.__name__, res)
return res
# def __set__(self, instance, value):一旦加上它变成数据描述符,就无法实现延时计算,因为数据描述符优先级高于实例属性
# pass

class Room:
def __init__(self, name, length, width):
self.name = name
self.length = length
self.width = width
@Lazyproperty #area = Lazyproperty(area),实现了给类增加描述符的功能
def area(self):
return self.length * self.width

@property
def area1(self):
return self.length * self.width

r1 = Room('restroom', 3, 8)
print(r1.area)#调用area会触发代理Lazyproerty的__get__方法
print(r1.__dict__)
print(r1.area)#当实例属性中存在area属性时,不再去找非数据描述符
print(r1.area)
print(r1.area)

print(Room.area)

 

class Foo:
@property
def AAA(self):
print('调用时运行')

@AAA.setter
def AAA(self, val):
print('设置时运行')

@AAA.deleter
def AAA(self):
print('删除时运行')
#只有在属性AAA定义property后,才能定义AAA.setter,AAA.deleter
f1 = Foo()
f1.AAA
f1.AAA = 11
class Foo:

def get_AAA(self):
print('调用时运行')
def set_AAA(self, val):
print('设置时运行')
def del_AAA(self):
print('删除时运行')

AAA = property(get_AAA, set_AAA, del_AAA)#get, set, delete顺序不能变

f1 = Foo()
f1.AAA
f1.AAA = 11
del f1.AAA
#type方法创建类
def __init__(self, name, age):
self.name = name
self.age = age
def test(self):
print('------')
res = type('Foo', (object,), {'x': 1, '__init__': __init__, 'test': test})
print(res)
print(res.__dict__)

f1 = res('cyx', 18)
print(f1.name)
f1.test()

 自定义元类

class MyType(type):
def __init__(self, a, b, c):
print('元类的构造函数')
def __call__(self, *args, **kwargs):
obj = object.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj

class Foo(metaclass=MyType):#执行Foo = MyType(Foo,'Foo', (object,), {})
def __init__(self, name):
self.name = name

f1 = Foo('cyx')#触发__call__执行
print(f1.name)
posted on 2018-05-24 21:41  吃猫粮的狗  阅读(146)  评论(0编辑  收藏  举报