python编程的三种方法论:面向过程-函数式编程(内置函数)-面向对象
过程:没有返回值的函数
面向过程:找到解决问题的入口,按照一个固定的流程去模拟解决问题的流程
举例:
第一步:搜索目标,按照要求到数据结构(字典)内检索合适的人物
第二步:表白,表白成功进入第三步,否则进入第二步。
第三步:恋爱,恋爱成功进入第四步,否则退回到第一步。
第四步:家长同意进入第五步,家长说她是你失散多年的妹妹,返回第一步
第五步:结婚
把一个大的问题分解成N个小的问题,每次解决问题从头开始一步一步执行这个流程,中间加上逻辑的判断,基本单位就是函数
第二种:函数式编程
1.函数式编程=编程语言定义的函数+数学意义的函数
2.函数式就是用编程语法去实现数学函数。这种函数内对象是永恒不变的,要么参数是函数,要么返回值是函数,没有for和while循环,所有的循环都由递归去实现,无变量的赋值(即不用变量去保存状态),无赋值即不改变。
(1)数学模型(数学模型代表一个数学逻辑):
y=2*x+1
(2)用python当中的def去实现上面的数学逻辑:
def cal(x): return 2*x+1
3.函数式编程的特征:
(1)不可变数据:不用变量保存状态,不修改变量
#非函数(面向过程)
a = 1 def incr_test1(): global a a+=1 return a incr_test1()
print(a)
输出:
2
#函数式
n=1 def incr_test(n): return n+1 print(incr_test(1))
输出:
2
(2)第一类对象:函数即“变量”
1.1函数名可以当作参数传递
def foo(n): print(n) def bar(name): print('my name is %s' %name)
foo(bar)
详解:
foo(bar)把函数bar直接当成实参传给形参foo(n),实际做的操作就是把bar赋值给n了,函数当成变量赋值了n=bar
输出:
<function bar at 0x006798A0>
1.2返回值可以是函数名
举例1
def foo(n): print(n) def bar(name): print('my name is %s' %name) foo(bar('xixi'))
详解:
foo(bar('xixi'))传值xixi给%name打印my name is xixi,下面没有返回值默认返回一个None,相当于把None放到foo()这个位置传给foo了
输出:
my name is xixi
None
举例2:
def bar(): print('from bar') def foo(): print('from foo') return bar n=foo() n()
详解:
n=foo()运行foo()函数return返回值bar赋值给n(),n()相当于执行bar()函数
输出:
from foo
from bar
举例3:
def hanle(): print('from handle') return hanle h=hanle() h()
详解:
运行h=hanle()直接打印from handle返回值返回自己h(hanle)
输出:
from handle
from handl
(3)尾调用优化(尾递归):在函数的最后一步调用另外一个函数(最后一行不一定是函数的最后一步)
举例:
#非尾递归
def cal(seq): if len(seq) == 1: return seq[0] head,*tail=seq return head+cal(tail) print(cal(range(100)))
详解:head+cal(tail)把cal(tail)拿到结果做一个+head在返回需要保存状态
输出:
4950
#尾递归
def cal(l): print(l) if len(l) == 1: return l[0] first,second,*args=l l[0]=first+second l.pop(1) return cal(l) x=cal([i for i in range(100)]) print(x)
输出:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[3, 3, 4, 5, 6, 7, 8, 9]
[6, 4, 5, 6, 7, 8, 9]
[10, 5, 6, 7, 8, 9]
[15, 6, 7, 8, 9]
[21, 7, 8, 9]
[28, 8, 9]
[36, 9]
[45]
45
4.内置函数:map映射,filter过滤,reduce归纳
(1)map函数:处理序列中的每个元素,得到的结果是一个‘列表’,该‘列表’元素个数及位置与原来一样
举例:完成列表[1,2,10,5,3,7]加1,减1,平方的操作
匿名函数lambda写法:
num_l=[1,2,10,5,3,7] def map_test(func, array): #func=lambda x:x+1 arrary=[1,2,10,5,3,7] ret = [] for i in num_l: res = func(i) #add_one(i) ret.append(res) return ret print(map_test(lambda x: x + 1, num_l)) #加1 print(map_test(lambda x:x-1,num_l)) #减1 print(map_test(lambda x:x**2,num_l)) #平方
输出:
[2, 3, 11, 6, 4, 8]
[0, 1, 9, 4, 2, 6]
[1, 4, 100, 25, 9, 49]
内置函数map的用法:
(1)内置函数map方式写法1:(加1)
num_l=[1,2,10,5,3,7] res=map(lambda x:x+1,num_l) #传的是内置函数 print(res) print(list(res))
分析:
print(res)输出1:<map object at 0x01E655B0>
内置函数map处理的结果<map object at 0x01E655B0>,是一个可迭代类型,map处理的结果是一个迭代器,只能迭代一次
print(list(res))输出2:[2, 3, 11, 6, 4, 8]
list方法处理可迭代对象
map函数用法详解:
俩个参数:res=map(lambda x:x+1,num_l)
第一个参数:lambda x:x+1处理方法,第二个参数num_l是一个可迭代对象(这个可迭代对象就是内部for循环遍历一下,把每一个遍历的值都交给前面的逻辑去处理)
(2)函数map方式写法:(减1)
num_l=[1,2,10,5,3,7] def reduce_one(x): return x-1 print(list(map(reduce_one,num_l))) #传的是有名函数
输出:[0, 1, 9, 4, 2, 6]
分析:
最后map得到的也是一个结果也是可迭代对象,用list转化成一个列表,map处理的结果就是一个列表
第二个参数不一定是列表,只要是可迭代对象就可以
(3)函数map方式:(转大写)
msg='wangxixi' print(list(map(lambda x:x.upper(),msg)))
输出:
['W', 'A', 'N', 'G', 'X', 'I', 'X', 'I']
分析:
相当于把msg里的值依次循环,传给lambda表达式,返回加到list列表当中
(2)filter函数:遍历序列中的每个元素,判断每个元素得到布尔值,如果是True则留下来
举例1:过滤掉名字不是_yi结尾的
匿名函数lambda方式写法
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi'] def filter_test(func,array): ret=[] for p in array: if not func(p): ret.append(p) return ret res=filter_test(lambda n:n.endswith('yi'),movie_people) print(res)
输出:
['shihsi']
内置函数filter写法:
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi'] print(filter(lambda n:not n.endswith('yi'),movie_people))
输出:<filter object at 0x01E35750>
分析:打印出内存地址,这个内存地址已经保存了列表
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi']
res=filter(lambda n:not n.endswith('yi'),movie_people) print(list(res))
输出:['shihsi']
filter函数功能:前面一个函数lambda n:not n.endswith('yi')是得一个布尔值,根据后面for循环movie_people可迭代对象,如果得到的是True保留,所以需要加一个not
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi']
print(list(filter(lambda n:not n.endswith('yi'),movie_people)))
输出:
['shihsi']
filter的用法详解:
俩个参数:lambda n:not n.endswith('yi'),movie_people
第一个参数:lambda n:not n.endswith('yi')是一个函数,第二个参数movie_people是一个可迭代对象,它会把可迭代对象以此进行for循环遍历,遍历完了拿出每一个元素交给前面的函数进行处理,函数处理的结果是一个布尔值,布尔值等于True保留下来
举例2:过滤到age小于18的
people=[ {'name':'xixi','age':1000}, {'name':'shishi','age':10000}, {'name':'yaoyao','age':9000}, {'name':'shishi','age':18}, ] print(list(filter(lambda p:p['age']<=18,people)))
输出:
[{'name': 'shishi', 'age': 18}]
(3)reduce函数:处理一个序列,然后把序列进行合并操作
举例1:数字列表[1,2,3,100]把所有的值加起来再加上初始值100
匿名函数lambda方式写法:
num_l=[1,2,3,100] def reduce_test(func,array,init=None): if init is None: res=array.pop(0) else: res=init for num in array: res=func(res,num) return res print(reduce_test(lambda x,y:x+y,num_l,100))
详解:res=array.pop(0)先拿到列表的第一个值,初始值设定默认init=None,判断当init=None没有初始值,否则:res=init
输出:
206
reduce函数写法:
num_l=[1,2,3,100]
print(reduce(lambda x,y:x+y,num_l,1)) #指定初始值1
print(reduce(lambda x,y:x+y,num_l)) #不指定初始值
详解:第一个参数函数lambda x,y:x+y,第二个是一个序列num_l,第三个是初始值1
输出:
107
106
举例:计算1到100的和
from functools import reduce print(reduce(lambda x,y:x+y,range(100),100)) #range(100)相当于得到list列表,然后x,y一次赋俩个值做x+y print(reduce(lambda x,y:x+y,range(1,101)))
输出:
5050
5050
5.常用内置函数
(1)abs():绝对值
print (abs(-45))
返回:45
(2)all():把序列所有元素做布尔运算如果都是返回true,否则返回false(元素除了是 0、空、FALSE外都算TRUE)
all(['a', 'b', 'c', 'd']) #列表list,元素都不为空或0
返回:True
all(['a', 'b', '', 'd']) #列表list,存在一个为空的元素
返回:False
(3)any():把序列所有元素做布尔运算如果都是返回true,如果有一个为True,则返回True,如全是0、空、FALSE则返回FALSE
any(['a', 'b', 'c', 'd']) #列表list,元素都不为空或0
返回:True
any(['a', 'b', '', 'd']) #列表list,存在一个为空的元素
返回:True
any([0, '', False]) #列表list,元素全为0,'',false
返回:False
(4)isinstance()函数:来判断一个对象是否是一个已知的类型
a = 2 isinstance (a,int) #判断a是否是int类型
返回:True
(5)bytes():把一个字符串转换成字节的形式(编码转换二进制)
name="你好" print(bytes(name,encoding='utf-8'))
返回:
b'\xe4\xbd\xa0\xe5\xa5\xbd'
decode方式解码:
print(bytes(name,encoding='utf-8').decode('utf-8'))
返回:你好
(6)chr():用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。
print chr(0x30), chr(0x31), chr(0x61) #十六进制
返回:0 1 a
print chr(48), chr(49), chr(97) #十进制
返回:0 1 a
(7)dir():打印某一个对象下面的各种方法名字
(8)divmod():除数和余数运算结果结合起来(做分页功能)
divmod(10, 3)
返回:(3, 1)---3总共分了3页余1页
(9) eval():
功能1:把字符串里的数据结构提取出来
dic_str1="{'name':'xixi'}" eval(dic_str1)
返回:{'name': 'xixi'}
功能2:把字符串里的数学运算做一遍
x=5 eval( '3 * x' )
返回:15
(10)hash():可hash的数据类型即不可变数据类型,不可hash的数据类型即可变数据类型
特性1:不管传入参数有多长最终结果长度固定
特性2:不能根据最终hash的最终结果反推出来
特性3:只要变量不变,得到的hash值结果都一样
hash('xixi') #字符串
返回:485502670088932892
hash(str(sorted({'1':1}))) #字典
返回:7666464346782421378
(11)help():打印方法的相关解释
(12)bin():把10进制转换成2进制,以字符串形式表示
bin(10)
返回:'0b1010'
(13)hex()函数:将10进制整数转换成16进制,以字符串形式表示
hex(255)
返回:'0xff'
(14)oct()函数:将一个整数转换成8进制字符串
oct(10)
返回:'012'
(15)globals()函数:会以字典类型返回当前位置的全部全局变量
a='xixi' print(globals()) #globals函数返回一个全局变量的字典,包括所有导入的变量。
返回:{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, 'a': 'xixi', '__package__': None}
(16)locals()函数:会以字典类型返回当前位置的全部局部变量
def runoob(arg): #第一个局部变量argz z = 1 #第二个局部变量z print (locals()) runoob(2)
返回:{'z': 1, 'arg': 2} #返回一个名字/值对的字典
(17)max()和min()函数:取最大最小值
功能1:简单列表比较
l=[1,2,3,-5,100] print(max(l)) print(min(l))
返回:
100
-5
功能2:比较复杂列表
l=[(5,'e'),(1,'c'),(2,'b'),(3,'d'),] #默认从第一个值开始比较5最大 print(list(max(l)))
返回:[5, 'e']
功能3:比较字典
age_dic={'age1':19,'age4':20,'age3':100,'age2':30} print(max(age_dic)) #默认比较的是字典的key,根据ASCII码逐步去比较大小 print(max(age_dic.values())) #比较的是字典的value print((max(zip(age_dic.values(),age_dic.keys()))) ) #(zip(age_dic.values(),age_dic.keys())相当于[(19,'age1'),(20,'age4'),(100,'age3') ]
返回:
age4
100
[100, 'age3']
功能4:比较复杂的字典
people=[{'name':'xixi','age':1000},{'name':'wang','age':1111},{'name':'shi','age':111},{'name':'yao','age':11},] print(max(people,key=lambda dic:dic['age'])) #for循环people把取出来的值都给lambda表达式,值都是小字典
返回:{'age': 1111, 'name': 'wang'}
注意:
1.max和min函数处理的是可迭代对象,相当于for循环取出每个元素进行比较,不通类型直接不能进行比较。
2.每个元素间进行比较,是从每个元素的第一个位置以此比较,如果第一位置分出大小,后面的都不需要比较,直接得出这俩元素的大小
(18)ord()函数:返回对应的ASCII数值
print(ord('a'))
返回:97
(19)pow()函数:返回 xy(x的y次方)的值。
print(pow(3,3))
返回:9
(20)reversed()函数:用于反向列表中元素
l=[1,2,3,4] print(list(reversed(l)))
返回:[4,3,2,1]
(21)round()函数:方法返回浮点数x的四舍五入值
print(round(3.5))
返回:4
(22)set()函数:创建一个无序不重复元素集
print(set('hello'))
返回:{'l','o','h','e'}
(23)slice()函数:实现切片对象,主要用在切片操作函数里的参数传递
l='hello' s1=slice(3.5) s2=slice(1,4,2) #取步长 print(l[s1]) print(l[s2])
返回:
lo
el
(24)sorted()函数:对所有可迭代的对象进行排序操作
功能1:简单列表排序
l=[3,2,1,5,7] #排序本质就是比较大小 print(sorted(l))
返回:[1,2,3,5,7]
功能2:字典排序
people=[{'name':'xixi','age':1000},{'name':'wang','age':1111},{'name':'shi','age':111},{'name':'yao','age':11},] print(sorted(people,key=lambda dic:dic['age'])) #for循环people把取出来的值都给lambda表达式,值都是小字典
返回:[{'age': 11, 'name': 'yao'}, {'age': 111, 'name': 'shi'}, {'age': 1000, 'name': 'xixi'}, {'age': 1111, 'name': 'wang'}]
(25)str()函数:将对象转化为字符串
print(str({'a':1}))
返回:"{'a':1}"
(26)sum()函数:对系列进行求和计算
l=[1,2,3,4] print(sum(l))
返回:10
(27)type()函数:查看数据类型
msg='123' if type(msg) is str: #判断msg是否是整型 msg=int(msg) #不是转换成int res=msg+1 print(res)
返回:124
(28)vars()函数:返回对象object的属性和属性值的字典对象
def test(): msg='西西西西西' print(vars()) test()
返回:{'msg': '西西西西西'}
(29)__import__()函数:用于动态加载类和函数
第三种:面向对象
1.类:是一种数据结构,就好比一个模型,该模型用来表述一类事物(动作与特征的结合),用它来生产真实的物体(实例)。类是来区分一个种类,提取这一个种类所共有的动作和特征,类是个抽象的概念,抽象共同的特征,共同的动作
2.对象:动作与特征的结合就是对象,对象是针对类产生的一个具体的存在,对象也有数据属性和函数属性
3.类与对象的关系:对象是有类产生的,上帝造人,首先有一个造人的模版,这个模版即人的类,然后上帝根据类的定义来生产一个个的人
4.实例化:由类生产对象的过程叫实例化,类实例化的结果就是一个对象,或者叫做一个实例(实例=对象)
5.面向对象设计(把数据属性和函数属性整合到一起形成类)
def cat(name,gender,type): #cat函数内嵌了三个子函数(cat函数定义的类,共性1都会叫共性2都会吃饭 def jiao(cat): #函数一:猫叫的动作 print('一只叫猫[%s]的猫,喵喵喵' % cat['name']) def chi_fan(cat): #函数二:猫吃饭的动作 print('一只[%s],正在吃饭' % cat['type']) def init(name,gender,type): #函数三init:功能用来初始化猫的方法(字典把动作和特征融合在一起) cat1 = { 'name':name, #接收名字 'gender': gender, #接收性别 'type': type, #接收种类 'jiao':jiao, 'chi_fan':chi_fan, } return cat1 #返回cat1这个字典,这个字典包含了猫的特征和猫的动作 return init(name,gender,type) d1=cat('白雪','母','暹罗猫') #d1是对象,d1的产生:执行cat这个类,传了三个参数会触发类当中的init(name,gender,type)初始化函数的运行,把猫的一堆属性和动作初始化好了,最后返回名字为白雪性别为母的种类为暹罗猫的猫 d2=cat('咪达','公','波斯猫') print(d1) #{'name': '白雪', 'gender': '母', 'type': '暹罗猫', 'jiao': <function cat.<locals>.jiao at 0x01E798A0>, 'chi_fan': <function cat.<locals>.chi_fan at 0x01E798E8>} print(d2) #{'name': '咪达', 'gender': '公', 'type': '波斯猫', 'jiao': <function cat.<locals>.jiao at 0x01E79858>, 'chi_fan': <function cat.<locals>.chi_fan at 0x01E794B0>} d1['jiao'](d1) #运行d1里[jiao]的内存地址加(d1) 运行结果:一只叫猫[白雪]的猫,喵喵喵 d2['chi_fan'](d2)
打印结果:
{'name': '白雪', 'gender': '母', 'type': '暹罗猫', 'jiao': <function cat.<locals>.jiao at 0x01E798A0>, 'chi_fan': <function cat.<locals>.chi_fan at 0x01E798E8>}
{'name': '咪达', 'gender': '公', 'type': '波斯猫', 'jiao': <function cat.<locals>.jiao at 0x01E79858>, 'chi_fan': <function cat.<locals>.chi_fan at 0x01E794B0>}
一只叫猫[白雪]的猫,喵喵喵
一只[波斯猫],正在吃饭
6.类相关的知识:
(1)初识类:在python中声明函数与声明类极其相似
声明函数:
def functionName(args):
'函数文档字符串'
函数体
声明类:
class 类名:
'类的文档字符串'
类里面的函数和方法
类和实例化举例:
class Chinese: '这是一个中国人的类' #类的文档字符串 pass print(Chinese) #运行结果:<class '__main__.Chinese'> class声明的类,__main__当运行这个文件时候的文件名下面的Chinese这个类 p1 = Chinese() #实例化会返回一个结果,相当于函数的return:Chinese()运行进行的是产生的一个实例用p1赋值。 print(p1) #运行结果:<__main__.Chinese object at 0x00624870>,由Chinese类产生的对象object内存地址是0x00624870
打印结果:
<class '__main__.Chinese'>
<__main__.Chinese object at 0x00624870>
(2)类的种类
在python中分新式类和经典类,python3中统一都是新式类。
所有类甭管是否显示声明父类,都有一个默认继承object父类
经典类:
class 类名:
pass
新式类:新式类和经典类实名的最大不同在于,所有新式类必须继承至少一个父类
class类名(父类):
pass
(3)类的属性:类是用来描述一类事物,类的对象指的是这一类事物中的一个个体,是物就要有属性,属性分为
1.数据属性:就是变量(特征)
2.函数属性:就是函数,在面向对象里通常成为方法
注意:类和对象均用点来访问自己的属性
(4)查看类属性
俩种方式查看类的属性
dir(类名):查出的是一个名字列表
类名.__dict__查出的是一个字典,key为属性名,value为属性值
class Chinese: #定义一个类Chinese '这是一个中国人的类' ren='谦虚' #给类定义了一个数据属性:ren='谦虚' def kan_da_shan(): #类下面定义函数属性1:一个共同的方法kan_da_shan() print('三五个人侃大山') def cha_dui(self): #类下面定义函数属性2:cha_dui(self) print('插到了前面') print(Chinese.ren) #这个调用的是Chinese下面的数据属性ren.打印结果:谦虚 Chinese.kan_da_shan() #调用Chinese下面的函数属性kan_da_shan()打印结果:三五个人侃大山 Chinese.cha_dui('xixi') #调用Chinese下面的函数属性cha_dui('xixi')打印结果:插到了前面 #print(dir(Chinese)) #dir加上类名查看类的属性列(表方式显示属性名字) #print(Chinese.__dict__) #通过.__dict__查看属性字典 print(Chinese.__dict__['ren']) #Chinese加点调用数据属性本质就是到自己的属性字典里去找东西,打印结果:谦虚 Chinese.__dict__['kan_da_shan']() #Chinese.__dict__['kan_da_shan']得到的是内存地址加上()运行得到结果:三五个人侃大山 Chinese.__dict__['cha_dui'](1) #Chinese.__dict__['cha_dui']得到的内存地址得到结果加上(1)运行得到结果:插到了前面 print(Chinese.__name__) #显示类名字:Chinese print(Chinese.__doc__) #显示类的文档:'这是一个中国人的类' print(Chinese.__module__) #显示这个类所在那个模块:__main__
输出结果:
谦虚
三五个人侃大山
插到了前面
谦虚
三五个人侃大山
插到了前面
Chinese
这是一个中国人的类
__main__
7.对象的相关知识
class Chinese: #定义一个类Chinese(类也有作用域的概念) '这是一个中国人的类' ren='谦虚' #给类定义了一个数据属性:ren='谦虚' def __init__(self,name,age,gender): #在类下定义了函数属性1:__init__,在类当中必须要有一个初始化函数,定义__init__这个初始化函数(里面写上数据属性,self代表自己,谁来实例化,self就是谁,self做了一个统一) print('我是初始化函数,我开始运行了') #用self.名字定义数据属性,self是实例自己,self.名字代表给这个实例赋予名字属性,名字属性就是数据属性=name,name是执行函数传进来的参数,class会自动帮你返回想要的self self.mingzi = name #把name封装到self这个实例自己了 #p1.mingzi=name self.nianji = age #把age封装到self这个实例自己了 #p1.nianji=age self.xingbie = gender #把gender封装到self这个实例自己了 print('我结束啦') #最终return的就是self,class自动帮着return最终返回的结果就是字典,字典里封装了mingzi,nianji,xingbie def kan_da_shan(): #类下面定义函数属性2:一个共同的方法kan_da_shan() print('三五个人侃大山') def cha_dui(self): #类下面定义函数属性3:cha_dui(self),self标明要传实例自己 print('%s 插到了前面' %self.mingzi) #%s %self.mingzi找数据属性打印结果:xixi 插到了前面
def eat_food(self, food): #类下面定义函数属性3:
print('%s 正在吃%s' % (self.mingzi, food)) #以上代码最终给了Chinese这个变量名,这段代码加载到内存中
#实例:内存空间存的就是字典,它的作用域就是def __init__(self,name,age,gender)里面的作用域,实例只有数据属性,函数属性跟类要,调用的方法都是在类里去找(好处实例通过引用方式去调用类里的函数节省内存) #只要是初始化函数名字定义成__init__这个名字,就可以用类名加括号运行这个类的时候会自动找到init函数运行,实例化的过程本身就是调用函数的过程,运行的过程就叫实例化的过程 p1=Chinese('xixi',18,'female') #Chinese加上括号代表运行这个类,实例化的结果产生一个实例返回值p1,会把p1传给__init__(self,name,age,gender)里的self,相当于p1=Chinese.__init__(p1,name,age,gender),给self传的p1,self就是实例本身 print(p1.__dict__) #查看p1的实例属性字典形式:{'mingzi': 'xixi', 'nianji': 18, 'xingbie': 'female'} print(p1.mingzi) #实例化出来看到字典了,有了字典可以调它的值返回mingzi:xixi #p1这个人只有数据属性没有函数属性 #p1调用数据属性,p1是一个实例,这个实例是由chinese这个类产生的,实例属性能访问到类属性依据函数的作用域 print(p1.ren) #Chinese会出发__init__这个函数,__init__这个函数作用域在,可以调到ren这个属性返回:谦虚 #调用类下面定义函数属性 Chinese.cha_dui(p1) #p1的实例调插队这个方法返回:xixi 插到了前面(实例一定能够访问类属性) #p1.kan_da_shan() #python会把p1传给kan_da_shan()这个函数的第一个位置参数没有所以会报错(类属性访问不了实例属性)
#从新定义个实例化p2
p2=Chinese('shishi',18,'男')
p2.eat_food('馅饼')
输出结果:
我是初始化函数,我开始运行了
我结束啦
{'mingzi': 'xixi', 'nianji': 18, 'xingbie': 'female'}
xixi
谦虚
xixi 插到了前面
shishi 正在馅饼
定义一个学校类:
class School: #定义一个类Scholl x=1 #类的数据属性 def __init__(self,name,addr,type): #类的函数属性(初始化函数__init__帮类实例化出一个具体的对象,用一个学校类来实例出具体的学校,一个具体的学校的属性有name,addr,type) self.Name=name #Name需要被存到属性字典里的key,name只是传过来的值 self.Addr=addr self.Type=type def tell_info(self): #类下面定义函数属性(查看学校的详细信息) print('学校的详细信息是:name:%s addr:%s' %(self.Name,self.Addr)) #通过调用%(self.Name,self.Addr) s1=School('八中','西城','公立') #School()实例化触发__init__传3个参数进去,赋值到s1 print(s1.__dict__) #查看实例的属性字典{'Name': '八中', 'Addr': '西城', 'Type': '公立'}(只有刚刚设定数据属性) print(School.__dict__) #类当中有数据属性 s1.tell_info() #实例调用类的方法返回:学校的详细信息是:name:八中 addr:西城 School.tell_info(s1) #类去调用不会自动调参数需要加s1返回:学校的详细信息是:name:八中 addr:西城
8.类属性与对象(实例)
(1)属性类属性增删改查
class Chinese: country='China' def __init__(self,name): self.name=name def play_ball(self,ball): #函数属性命名规则遵循动词加名词play_ball(self,ball) print('%s 正在打 %s' %(self.name)) #查看类 print(Chinese.country) #返回China #修改类 Chinese.country='Japan' #修改类 print(Chinese.country) #返回Japan #修改类后用这个类生成实例 p1=Chinese('xixi') #传一个名字进去 print(p1.__dict__) #{'name': 'xixi'} print(p1.country) #p1调到了类Japan #增加类 Chinese.ren='好人' print(Chinese.ren) #查看类返回:好人 print(p1.ren) #p1访问到ren,返回:好人 #删除类 del Chinese.ren del Chinese.country print(Chinese.__dict__) #删除后没有 #print(Chinese.country) #删除类后调用country会报错 ####################函数属性的增删改查 def eat_food(self,food): print('%s 正在吃%s' %(self.name,food)) #给类增加函数属性 Chinese.eat=eat_food print(Chinese.__dict__) #查看是否把函数属性加进去了吗 p1.eat('西瓜') #运行p1返回:xixi 正在吃西瓜 #修改函数属性 def test(self): print('test') Chinese.play_ball=test p1.play_ball() #修改后用p1去掉用play_ball() 返回test
打印结果:
China
Japan
{'name': 'xixi'}
Japan
好人
好人
{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x01E48978>, 'play_ball': <function Chinese.play_ball at 0x01E48930>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None}
{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x01E48978>, 'play_ball': <function Chinese.play_ball at 0x01E48930>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'eat': <function eat_food at 0x0056B6A8>}
xixi 正在吃西瓜
test
(2)实例属性的增删改查
class Chinese: country='China' def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball)) p1=Chinese('xixi') #创建实例p1 #查看实例属性字典 print(p1.__dict__) #返回:{'name': 'xixi'} #查看实例属性 print(p1.name) #实例数据属性返回:xixi print(p1.play_ball) #实例访问函数属性(是类的属性)返回类的绑定的方法:<bound method Chinese.play_ball of <__main__.Chinese object at 0x02193870>> #给实例增加数据属性方式 p1.age=18 print(p1.__dict__) #增加后:{'name': 'xixi', 'age': 18} print(p1.age) #访问这个age:18 #修改 p1.age=28 print(p1.__dict__) #修改属性后:{'name': 'xixi', 'age': 28} print(p1.age) #访问这个age:28 #删除 del p1.age print(p1.__dict__) #删除后:{'name': 'xixi'}
打印结果:
{'name': 'xixi'}
xixi
<bound method Chinese.play_ball of <__main__.Chinese object at 0x0083B590>>
{'name': 'xixi', 'age': 18}
18
{'name': 'xixi', 'age': 28}
28
{'name': 'xixi'}
9对象与实例属性
(1)实例p1增加后类和实例的变化
class Chinese: country='China' def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball)) p1=Chinese('xixi') print(p1.country) #返回结果是China 访问的是类的 #把p1.country改为韩国人 p1.country='韩国人' #在p1字典里新增了一个韩国人,跟类的字典没有关系 print('类的--->',Chinese.country) #返回:类的---> China print('实例的-->',p1.country) #返回:实例的--> 韩国人。在p1自己的字典里面加东西了,所以实例的修改了
返回:
China
类的---> China
实例的--> 韩国人
(2)不是实例属性就是普通变量
country='中国' class Chinese: def __init__(self,name): self.name=name print(country) #没有加点既不是类的属性,也不是实例属性就是普通变量,如果找不到会到父节点找返回:中国 def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball)) p1=Chinese('xixi') #实例化触发def __init__(self,name)函数运行
返回:
中国
(3)类内部和类外部的应用
country='最外层' class Chinese: country = '中国' def __init__(self,name): self.name=name print(country) #没有加点既不是类的属性,也不是实例属性就是普通变量,如果找不到会到上层节点找 def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball)) p1=Chinese('xixi') #实例化触发def __init__(self,name)函数运行返回触发到:最外层 print(Chinese.country) #调用中国类加点country返回:中国 p1=Chinese('xixi') #实例化触发def __init__(self,name)函数运行返回触发到:最外层 print(Chinese.country) #调用实例属性p1返回:中国
返回:
最外层
中国
最外层
中国
(4)改类里的新增属性
class Chinese: country='China' l=['a','b'] #列表放入俩个值 def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball)) p1=Chinese('xixi') print(p1.l) #p1自己位置没有找类打印:['a','b'] #修改类但不是赋值,不是新增属性 p1.l.append('c') #给类新增属性,返回值:['a', 'b', 'c'] print(Chinese.l)
返回:
['a', 'b']
['a', 'b', 'c']
面向对象静态属性---类方法---静态方法---类的组合
1.静态属性:把函数封装成数据属性,让外部调用的时候感受不到内部的逻辑,定义静态属性内部有一个self,这个self就是实例本身,静态属性既可以访问实例属性又可以访问类属性
(1)实例化调用函数属性实现求面积
class Room: #定义房间的类 def __init__(self,name,owner,width,length,heigh): #房间的属性(初始化函数) self.owner=owner #房间的主人 self.name = name #房间的名字 self.width=width #房间的宽度 self.length=length #房间的长度 self.heigh=heigh #房间的高度 #求面积 r1 = Room('主卧','宝宝',10, 10, 10) #把参数传到房间属性里 print('%s 住在 %s 总面积是%s' %(r1.owner,r1.name,r1.width*r1.length))
返回:
宝宝 住在 主卧 总面积是100
(2)封装方法方式把公共部分放到类里实现求面积
class Room: #定义房间的类 def __init__(self,name,owner,width,length,heigh): #房间的属性(初始化函数) self.owner=owner #房间的主人 self.name = name #房间的名字 self.width=width #房间的宽度 self.length=length #房间的长度 self.heigh=heigh #房间的高度 def cal_area(self): #公共的计算面积函数(函数属性) print('%s 住在 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)) #求面积 r1 = Room('主卧','宝宝',10, 10, 10) #把参数传到房间属性里 r2 = Room('次卧','西西',5, 5, 10) #求面积方式2: r1.cal_area() #r1实例调用cal_area()方法实现 r2.cal_area()
返回:
宝宝 住在 主卧 总面积是100
西西 住在 次卧 总面积是25
(3)静态属性方式把公共部分放到类里实现求面积(@property跟实例绑定)
class Room: #定义房间的类 def __init__(self,name,owner,width,length,heigh): #房间的属性 self.owner=owner #房间的主人 self.name = name #房间的名字 self.width=width #房间的宽度 self.length=length #房间的长度 self.heigh=heigh #房间的高度 @property #class里提供的@property把函数属性变成数据属性 def cal_area(self): #公共的计算面积函数 return self.width * self.length #return返回长度乘以宽度 #求面积 r1 = Room('厕所','宝宝',10, 10, 10) r2 = Room('次卧','西西',5, 5, 10) #利用@property print(r1.cal_area) #@r1通过点方式调用cal_area属性 print(r1.name) print(r2.cal_area) print(r2.owner)
输出:
100
厕所
25
西西
2.类方法:cls代表类,能访问到数据属性和函数属性,跟实例没有关系,只是类调用自己的方法时候用(@classmethod跟类绑定)
(1)通过实例化方式获取到类的详细信息
class Room: #定义房间的类 tag=1 #定义一个数据属性 def __init__(self,name,owner,width,length,heigh): #房间的属性 self.owner=owner #房间的主人 self.name = name #房间的名字 self.width=width #房间的宽度 self.length=length #房间的长度 self.heigh=heigh #房间的高度 def tell_info(self): #(self)的定义把类的函数属性跟实例捆绑到一块了 print('类的详细信息',self.tag) print(Room.tag) #类调用自己的数据属性返回:1 #调用类的详细信息,由于类调用自己的函数属性,跟实例捆绑到一块了,所以先需要实例化 r1 = Room('主卧','宝宝',10, 10, 10) #必须先做实例化 Room.tell_info(r1) #才能调用类的详细信息
返回:
1
类的详细信息 1
(2)通过类方法调用类的详细信息(自动做一个传递参数)
class Room: #定义房间的类 tag=1 #定义一个数据属性 def __init__(self,name,owner,width,length,heigh): #房间的属性 self.owner=owner #房间的主人 self.name = name #房间的名字 self.width=width #房间的宽度 self.length=length #房间的长度 self.heigh=heigh #房间的高度 @classmethod #@classmethod专门供类使用的类方法 def tell_info(cls): #cls这个参数接收的是一个类名Room print(cls) #打印的是Room类,返回:<class '__main__.Room'> print('类的详细信息', cls.tag) #cls就是类调用tag类的数据属性,不能访问实例 #类方法的定义只为了类去调用 Room.tell_info() #只要是类方法通过类名字Room点类方法的方式调用就会自动做一个传递参数的操作
返回:
<class '__main__.Room'>
类的详细信息 1
(3)通过类方法调用类的详细信息(加参数)
class Room: #定义房间的类 tag=1 #定义一个数据属性 def __init__(self,name,owner,width,length,heigh): #房间的属性 self.owner=owner #房间的主人 self.name = name #房间的名字 self.width=width #房间的宽度 self.length=length #房间的长度 self.heigh=heigh #房间的高度 @classmethod #@classmethod专门供类使用的类方法 def tell_info(cls,x): #cls这个参数接收的是一个类名Room,x是自己加的参数 print(cls) #打印的是Room类,返回:<class '__main__.Room'> print('类的详细信息', cls.tag,x) #cls就是类调用tag类的数据属性返回:1 #用类调用类方法不用传参数了 Room.tell_info(10) #只要是类方法通过类名字Room点类方法的方式调用就会自动做一个传递参数Room的操作,在自己传一个10
返回:
<class '__main__.Room'>
类的详细信息 1 10
3.静态方法:只是名义上归属类管理,不能使用变量和实例变量,是类的工具包( @staticmethod跟类跟实例都没关系了)
class Room: #定义房间的类 tag=1 #定义一个数据属性 def __init__(self,name,owner,width,length,heigh): #房间的属性 self.owner=owner #房间的主人 self.name = name #房间的名字 self.width=width #房间的宽度 self.length=length #房间的长度 self.heigh=heigh #房间的高度 @staticmethod #类的工具包 def bathe(a, b, c): #定义洗澡属性(没有self调用不了类属性和实例属性) print('%s %s %s正在洗澡' % (a, b, c)) # print(Room.__dict__) #在类的属性字典里有:'__weakref__': <attribute '__weakref__' of 'Room' objects>, ' #实例化调用 r1 = Room('主卧','宝宝',10, 10, 10) print(r1.__dict__) #实例只有数据属性,没有函数属性
#
Room.bathe('xi','yao','shi') #只能访问实例变量返回:xi yao shi正在洗澡
{'__module__': '__main__', 'tag': 1, '__init__': <function Room.__init__ at 0x02198978>, 'bathe': <staticmethod object at 0x0048B590>, 'test': <function Room.test at 0x021988A0>, '__dict__': <attribute '__dict__' of 'Room' objects>, '__weakref__': <attribute '__weakref__' of 'Room' objects>, '__doc__': None}
{'owner': '宝宝', 'name': '主卧', 'width': 10, 'length': 10, 'heigh': 10}
4.类的组合:组合指的是在一个类中以另外一个类的对象作为数据属性
类的组合的用途:
(1)类跟类之间没有共同点,但他们之间是有关联的
(2)小的组成大的
1)简单定义一个组合:
class Hand: #定义类Hand pass class Foot: #定义类Foot pass class Trunk: #定义类Trunk pass class Head: #定义类Head pass class Person: #定义一个大类Person(里面由一个个小类组合而成的) def __init__(self,id_num,name): self.id_num=id_num #定义自己身份证属性 self.name=name #定义自己名字属性 self.hand=Hand() #直接调用Hand类 self.foot=Foot() #直接调用Foot类 self.trunk=Trunk() #直接调用Trunk类 self.head=Head() #直接调用Head类 p1=Person('123456','xixi') #创建实例p1用Person类实例化传id_num和name print(p1.__dict__) #查看p1实例属性字典
返回:
{'id_num': '123456', 'name': 'xixi', 'hand': <__main__.Hand object at 0x0041B5F0>, 'foot': <__main__.Foot object at 0x005A0470>, 'trunk': <__main__.Trunk object at 0x006B80F0>, 'head': <__main__.Head object at 0x006B8130>}
2)定义一个组合一个大的类由一些小的类组合而成:
class School: #定义一个School学校类 def __init__(self,name,addr): self.name=name #属性名字 self.addr=addr #属性地址 def zhao_sheng(self): #定义一个招生zhao_sheng方法 print('%s 正在招生' %self.name) class Course: #定义一个Course课程类 def __init__(self,name,price,period,school): self.name=name #名字属性 self.price=price #价格属性 self.period=period #周期属性 self.school=school #学校信息属性 s1=School('西西学校','北京') #实例化完后把School传到学校类 s2=School('西西学校','上海') s3=School('西西学校','西安') #c1=Course('数学','100元','一小时','西西学校 北京') #实例化Course课程类 c1=Course('数学','100元','一小时',s1) #s1替换'西西学校 北京' print(c1.__dict__) #s1学校传过来一个对象'school': <__main__.School object at 0x0065B590>} print(c1.school.name) #查找数学这门课程的校区名字 print(c1.school.addr) #查找数学这门课程的校区地址
返回:
{'name': '数学', 'price': '100元', 'period': '一小时', 'school': <__main__.School object at 0x0024B590>}
西西学校
北京
3)定义类组合:课程关联老师跟学校 老师要关联学校:
class School: #定义一个School学校类 def __init__(self, name, addr): self.name = name #学校的名字 self.addr = addr #地址 class Course: #定义一个Course课程类 def __init__(self, name, price, period, teacher,school): self.name = name #课程名字 self.price = price #价格属性 self.period = period #周期属性 self.teacher = teacher # 老师信息 self.school = school #学校信息属性 class Teacher: # 定义一个Teacher老师类 def __init__(self,name,laborage,school): self.name=name #老师名字 self.laborage=laborage #老师工资 self.school = school #学校信息属性 s1 = School('交通大学', '北京') #实例化完后把School传到学校类 t1 = Teacher('王老师','6000元',s1) #实例化完把Teacher c1 = Course('数学', '100元', '一小时',t1,s1) #s1替换'西西学校 北京' #课程关联老师跟学校 print(c1.__dict__) # s1传过对象'school': <__main__.School object at 0x0065B590>} t1传过对象'teacher': <__main__.Teacher object at 0x0025B5F0> print('地址:',c1.school.addr,'学校:',c1.school.name,'老师名:',c1.teacher.name,'工资:',c1.teacher.laborage) #老师关联学校 print(t1.__dict__) print('地址:',t1.school.addr,'学校:',t1.school.name)
返回:
{'name': '数学', 'price': '100元', 'period': '一小时', 'teacher': <__main__.Teacher object at 0x005AB5F0>, 'school': <__main__.School object at 0x005AB590>}
地址: 北京 学校: 交通大学 老师名: 王老师 工资: 6000元
{'name': '王老师', 'laborage': '6000元', 'school': <__main__.School object at 0x005AB590>}
地址: 北京 学校: 交通大学
面向对象的三大特性-继承-多态-封装
继承:类的继承跟现实生活中的父亲 儿子 孙子 重孙子继承关系一样 父亲类又称为基类
1.python中的类的继承分为单继承和多继承
class father: #定义了一个father类 pass class mother: #定义了一个mother类 pass class son1(father): #son1类单继承(继承了father类) pass class son2(father,mother): #son12类多继承(继承了father和mother类) pass
(1)子类单继承父类(子类继承了父类的所有属性)
class Father: '这个是爸爸类' money='100万' #给类定义了一个数据属性:money='100万' def __init__(self,name): #初始化函数定义一个属性为name print('爸爸') self.name=name def hit_son(self): #定义函数属性 print('%s 正在打儿子' %self.name) class Son(Father): #定义Son单继承father类 pass print(Son.money) #查看Son类是否能继承父类的数据属性打印:100万 print(Father.__dict__) #查看Father实例属性字典 print(Son.__dict__) #查看Son实例属性字典返回:{'__module__': '__main__', '__doc__': None}(在类的属性字典里没有父属性) s1=Son('美美') #s1实例化本来要触发的是Son类的init方法,由于继承关系找的是Father类的init方法返回:爸爸 print(s1.name) #相当于把def __init__(self,name):里的方法拿到Son类里去执行返回:美美 print(s1.money) #通过s1也能找到父类里的数据属性返回:100万 s1.hit_son() #相当于把def hit_son(self):里的方法拿到Son类里去执行返回:美美 正在打儿子
返回:
100万
{'__module__': '__main__', '__doc__': '这个是爸爸类', 'money': '100万', '__init__': <function Father.__init__ at 0x00638978>, 'hit_son': <function Father.hit_son at 0x00638930>, '__dict__': <attribute '__dict__' of 'Father' objects>, '__weakref__': <attribute '__weakref__' of 'Father' objects>}
{'__module__': '__main__', '__doc__': None}
爸爸
美美
100万
美美 正在打儿子
(2)子类单继承父类(子类定义的属性如果跟父类重名了,优先调用自己的属性)
class Father: '这个是爸爸类' money='100万' #给类定义了一个数据属性:money='100万' def __init__(self,name): #初始化函数定义一个属性为name print('爸爸') self.name=name class Son(Father): #定义Son单继承father类 money = '1万' print(Son.money) #子类定义的属性跟父类重名,优先调用自己的属性返回:1万 print(Father.money) #父类调用自己的数据属性返回:100万
返回:
1万
100万
2.什么时候用继承
(1)当类之间有显著的不通,并且较小的类是较大的类所需要的组件时,用组合比较好
(2)当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
3.继承和派生
继承描述了子类属性从祖先类继承这一种方式
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系
class Animal: #定一个父类Animal动物类 def eat(self): #定义方法吃 print("%s 吃" %self.name) def drink(self): #定义方法喝 print("%s 喝" %self.name) def sleep(self): #定义方法睡 print("%s 睡" %self.name) class Cat(Animal): #定义猫的类Cat继承动物类Animal def __init__(self, name): #猫类定义自己的初始化方法--->派生成新的 self.name = name self.breed = '猫' def cry(self): #猫类派生出自己的函数属性 print('喵喵') class Dog(Animal): #定义狗的类Dog继承动物类Animal def __init__(self, name): #狗类定义自己的初始化方法--->派生成新的 self.name = name self.breed = '狗' def cry(self): #狗类派生出自己的函数属性 print('汪汪') #执行 c1 = Cat('暹罗猫') #c1实例化一个猫继承Animal父类里的吃 c1.eat() c2 = Cat('波斯猫') #c2实例化一个猫继承Animal父类里的喝 c2.drink() d1 = Dog('泰迪狗') #d1实例化一个狗继承Animal父类里的吃 d1.eat() d2 = Cat('藏獒狗') #d2实例化一个狗继承Animal父类里的喝 d2.drink()
返回:
暹罗猫 吃
波斯猫 喝
泰迪狗 吃
藏獒狗 喝
4.继承的含义
(1)继承基类的方法,并做出自己的改变或者扩展(代码重用)
(2)声明某个子类兼容于某基类,定义一个接口,子类继承接口类,并且实现接口中定义的方法
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”---这个程序设计上,叫做归一化。归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合--就好像linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存,磁盘,网络还是屏幕
第二种方式实现继承的含义(实现一切皆文件限定硬盘和内存里都应该有一个读方法和写方法)
import abc #abc模块实现接口继承 #接口相当一个函数,接口继承定义一个All_file基类(父类),利用装饰器的方式基类当中把自己方法定义成接口函数 class All_file(metaclass=abc.ABCMeta): #接口继承首先定义一个基类(父类)All_file,利用metaclass=abc.ABCMeta)就可以给类下面的属性加装饰器 @abc.abstractmethod #@abc.abstractmethod加上装饰器后下面的方法不用具体实现 def read(self): #@abc.abstractmethod加上装饰器后自己方法就定义成接口函数 '实现接口继承后子类必须实现读功能' pass #接口类目的是规范子类所以基类里的方法没必要实现 @abc.abstractmethod def write(self): '实现接口继承后子类必须实现写功能' pass #实现接口继承后,子类必须实现这俩方法,不实现就没办法实例化 class Disk(All_file): #定义Disk硬盘类:Disk类就完全被严格限定了 def read(self): #read方法 print('disk read') def write(self): #write写方法 print('disk write') class Mem(All_file): #定义Mem内存类:Mem类就完全被严格限定了 def read(self): #read方法 print('mem read') #def write(self): #write写方法 # print('mem write') #实现接口继承后实例化大家都被归一化,也就是一切皆文件的思想 m1=Disk() #实例化Disk这个类 m1.read() #m1调用read方法 m1.write() #m1调用write方法 #m2=Mem() #会报错,因为Mem必须要有read方法和writ方法才能实例化
返回:
disk read
disk write
上面这个例子的好处就是方便,操作一切事务调俩个方法就行了,一个是读一个是写,作为调用者不用关心调用的是什么东西里面是怎么实现的,只要是兼容基类的接口就可以
5.继承的顺序
(1)python的类可以继承多个类
(2)python的类如果继承了多个类,那么其寻找方法的方式有俩种:深度优先和广度优先
(3)深度优先:A类继承了B类和C类这俩个类的话,B类和C类又继承了D这个类,如果在A类中去找一个属性,A类的查找顺序是先找B类再找D类,如果都没有的话再找C类,就是一下先找到头,没有的话再找第二个类(基类没有任何继承关系的情况下是经典类,当类是经典类时,多继承情况下,会按照深度优先方式查找)
python2深度优先经典类继承顺序:定义一个基类A,B类继承A类,C类继承A类,D类继承B类,E类继承C类,F类继承D类和E类
#coding:utf-8 class A: #定义一个基类A def test(self): print('A') class B(A): #B类继承A类 #def test(self): # print('B') pass class C(A): #C类继承A类 #def test(self): # print('C') pass class D(B): #D类继承B类 #def test(self): # print('D') pass class E(C): #E类继承C类 #def test(self): # print('E') pass class F(D,E): #F类继承D类和E类 #def test(self): # print('F') pass f1=F() f1.test() #经典类:F类当中没有会找D类->D类当中没有会找B类->B类当中没有会找基类A类(如果A类当中没有在返回来找E类再找C类,C类当中没有会报错了)
返回:
A
(4)广度优先:A类继承了B类,B类又继承了C类和D类,如果在A类中去找一个属性,A类的查找顺序是先找B类,如果都没有的话再找C类,C类没有的话再找D类(当父类继承了object类就是新式类,当类是新式类时,多继承情况下,会按照广度优先方式查找)
python3新式类广度优先方法:定义一个基类A,B类继承A类,C类继承A类,D类继承B类,E类继承C类,F类继承D类和E类
class A: #定义一个基类A def test(self): print('A') class B(A): #B类继承A类 #def test(self): # print('B') pass class C(A): #C类继承A类 def test(self): print('C') class D(B): #D类继承B类 #def test(self): # print('D') pass class E(C): #E类继承C类 #def test(self): # print('E') pass class F(D,E): #F类继承D类和E类 #def test(self): # print('F') pass f1=F() f1.test() #新式类:F类当中没有会找D类->D类当中没有会找B类->B类当中没有会找E类->E类当中没有会找C类返回C(如果C类当中没有才会找最后的基类A,最后基类A类在没有会报错) print(F.__mro__) #查看MRO列表
返回结果:
C
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
(3)继承详解:python对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,MRO列表遵循三条准则:
1.子类会先于父类被检查(先从自己身上找)
2.多个父类会根据它们在列表中的顺序被检查(会在MRO列表中按顺序来)
3.如果对下一个类存在俩个合法的选择,选择第一个父类
6.在子类中调用父类方法
方法一:bus子类调用Vehicle父类__init__初始化方法和run函数属性方法
class Vehicle: #定义基类Vehicle交通工具 Country='China' #定义一个数据属性Country='China' def __init__(self,name,speed,load,power): #定义初始化函数name名字speed速度load负载power能源 self.name=name self.speed=speed self.load=load self.power=power def run(self): #定义一个run运动函数属性 print('开动啦') #子类 class bus(Vehicle): #定义一个子类bus公共汽车继承基类Vehicle交通工具 def __init__(self,name,speed,load,power,line): #子类自己定义初始化函数name名字speed速度load负载power能源 Vehicle.__init__(self,name,speed,load,power) #子类bus公共汽车调用父类Vehicle交通工具里的__init__方法(参数写上父类的参数) self.line=line #bus类自己封装的line参数 def show_info(self): #bus子类派生一个show_info方法(打印信息) print(self.name,self.speed,self.load,self.power,self.line) def run(self): #子类生一个run运行方法 Vehicle.run(self) #子类bus公共汽车调用父类Vehicle交通工具里的run方法后返回:开动啦 print('%s %s 开动啦' %(self.name,self.line)) #实例化子类自己的show_info方法 line44=bus('北京公交','30m/s','50人','汽油','44路公交车') #调用自己的方法 line44.show_info() #实例化后调用自己的show_info方法 #实例化子类自己的run方法 line44.run() #实例化后调用run方法返回:北京公交 44路公交车 ,开动啦
返回:
北京公交 30m/s 50人 汽油 44路公交车
开动啦
北京公交 44路公交车 开动啦
方法二:super方法调用父类的方法
(1)不用再写父类名字
(2)不用再传self参数默认给传进去了
class Vehicle: #定义基类Vehicle交通工具 Country='China' #定义一个数据属性Country='China' def __init__(self,name,speed,load,power): #定义初始化函数name名字speed速度load负载power能源 self.name=name self.speed=speed self.load=load self.power=power def run(self): #定义一个run运动函数属性 print('开动啦') #子类 class bus(Vehicle): #定义一个子类bus公共汽车继承基类Vehicle交通工具 def __init__(self,name,speed,load,power,line): #子类自己定义初始化函数name名字speed速度load负载power能源 #Vehicle.__init__(self,name,speed,load,power) #子类bus公共汽车调用父类Vehicle交通工具的方法(参数写上父类的参数) super().__init__(name,speed,load,power) #super方法不用写父类名字调用父类Vehicle交通工具的方法(参数写上父类的参数) #super(__class__,self).__init__(name,speed,load,power) #super另一种写法 self.line=line #bus类自己封装的line参数 def show_info(self): #bus子类派生一个show_info方法(打印信息) print(self.name,self.speed,self.load,self.power,self.line) def run(self): #子类生一个run运行方法 #Vehicle.run(self) #子类bus公共汽车调用父类Vehicle交通工具里的run方法后返回:开动啦 super().run() #super()就可以调用父类的构造方法 print('%s %s 开动啦' %(self.name,self.line)) line44=bus('北京公交','30m/s','50人','汽油','44路公交车') #调用自己的方法 line44.show_info() #实例化后调用自己的show_info方法 line44.run() #实例化后调用run方法返回:北京公交 44路公交车 ,开动啦
返回:
北京公交 30m/s 50人 汽油 44路公交车
开动啦
北京公交 44路公交车 开动啦
多态:由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
类的继承有俩层意义改变和扩展,多态就是类的这俩层意义的一个具体的实现机制,父类改变或扩展子类要有机制体现出来,体现出父类里改变了什么东西,到底继承了父类的那些特性,这个实现机制就是多态,多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类
多态是运行时候的绑定状态,允许重载及运行时类确定和验证,继承同一个父类
多态是怎么产生的:有一个基类,不同的类继承基类获得了多态的第一个关键因素就是不同的类有了相同的方法,由这不同的类实例化出的实例可以去调用这相同的方法
class Matter: #定义基类matter物质 def __init__(self,name,temperature): #定义初始化函数传了name名字和temperature温度 self.name=name self.temperature=temperature def turn(self): #基类里定义了一个函数属性方法叫turn转换 if self.temperature < 0: print('温度小于0度凝结成[%s]' %self.name) elif self.temperature > 0 and self.temperature < 100: print('温度大于0度小于100度形成了[%s]' %self.name) elif self.temperature > 100: print('温度大于100汽化成了[%s]' %self.name) #通过继承行程三种形态的水 class Water(Matter): #定义子类水继承基类Matter pass class Ice(Matter): #定义子类冰继承基类Matter pass class Steam(Matter): #定义子类水蒸气继承基类Matter pass #实例化w1水i1冰s1蒸汽 w1=Water('水',5) #Water产生的实例w1用的turn方法是调用的基类的方法 i1=Ice('冰',-5) s1=Steam('蒸汽',105) #形成多态方式一:调用wi,i1,si不同对象的相同方法turn,但是他们实现的过程不一样就是多态 w1.turn() i1.turn() s1.turn() print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') #形成多态方式二:用同一个函数调用不同的对象 def func_3(obj): #定义统一函数func_3传一个obj obj.turn() #调的时候就是obj点基类里的turn func_3(w1) #---->w1.turn() func_3(i1) #---->i1.turn() func_3(s1) #---->s1.turn()
返回:
温度大于0度小于100度形成了[水]
温度小于0度凝结成[冰]
温度大于100汽化成了[蒸汽]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
温度大于0度小于100度形成了[水]
温度小于0度凝结成[冰]
温度大于100汽化成了[蒸汽]
多态实际上是依附于继承的俩种含义:改变和扩展本人就是意味着必须有机制去自动选用你改变扩展过的版本,故无多态,则俩种含义就不可能实现,所以多态实质上是继承的实现细节
封装:抛开面向对象,单去想什么是装,就是拿一个麻袋,把小猫小狗小兔子一起装进麻袋,封就是把麻袋封上口子。在面向对象中这个麻袋就是你的类或者对象,类或者对象这俩麻袋内部装了数据属性和函数属性,那么对于类和对象来说,封的概念从何而来,其实封的概念代表隐藏
1.第一个层面的封装:类就是麻袋,这本身就是一种封装
定义一个封装类:fengzhuang.py
class People: #定义一个类People人(相当于一个麻袋把数据属性star和__init__和get_id俩个方法装进去了,给用户看到的只有一个类名) star='earth' #定义一个数据属性earth地球 def __init__(self,id,name,age,salary): #定义初始化函数 self.id=id self.name=name self.age=age self.salary=salary def get_id(self): #定义一个可以获取id号和名字 print('我是私有方法,我的id是[%s],我的名字是[%s]' %(self.id,self.name)) #print(People.__dict__) #查看装了什么东西
使用者使用fengzhuang.py里的People这个类(但不知道people这个类具体做了那些事)
from fengzhuang import People p1=People('18','xixi','18',999) p1.get_id()
返回:
我是私有方法,我的id是[18],我的名字是[xixi]
2.第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问
(1)任何以单下划线开头的名字都应该是内部的,私有的
class People: #定义一个类People人 _star='earth' #单下划线开头隐藏数据属性earth地球 def __init__(self,id,name,age,salary): #定义初始化函数 self.id=id self.name=name self.age=age self.salary=salary def get_id(self): #定义一个可以获取id号和名字 print('我是私有方法,我的id是[%s],我的名字是[%s]' %(self.id,self.name)) #外部在使用的时候 p1=People('18','xixi','18',999) print(p1._star) #p1外部可以调用到单下划线开头是可以调用的到的
返回:
earth
单下划线开头的动态导入模块
在mk目录下创建m1模块
print('模块导入成功') def test1(): print('调用到了test1函数') def _test2(): #在m1这个模块test2是私有属性 print('调用到了test2函数')
导入这个模块
#from mk.m1 import * #用*的方式是无法导入以单下划线私有开头的 from mk.m1 import test1,_test2 test1() _test2()
返回:
模块导入成功
调用到了test1函数
调用到了test2函数
(2)任何以双下划线开头的名字
class People: #定义一个类People人 __star='earth' #双下划线开头隐藏数据属性earth地球 def __init__(self,id,name,age,salary): #定义初始化函数 self.id=id self.name=name self.age=age self.salary=salary def get_id(self): #定义一个可以获取id号和名字 print('我是私有方法,我的id是[%s],我的名字是[%s]' %(self.id,self.name)) #外部在使用的时候 p1=People('18','xixi','18',999) #print(p1.__star) #p1外部可以调用到双下划线开头是调用不到的 print(p1._People__star) #只要是双下划线开头的属性,pyton自动做重命名操作:_People__star所以访问方式
返回:
earth
3.第三个层面的封装:明确区分内外,内部实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用
(1)接口函数
class People: #定义一个类People人 __star='earth' #双下划线开头隐藏数据属性earth地球 def __init__(self,id,name,age,salary): #定义初始化函数 print('私有', self.__star) #把数据属性封装到类的内部变成私有的了 self.id=id self.name=name self.age=age self.salary=salary def get_id(self): #定义一个可以获取id号和名字 print('我是私有方法,我的id是[%s],我的名字是[%s]' %(self.id,self.name)) #访问函数(接口函数) def get_star(self): #外部要调用的时候无法直接使用,需要在内部类上面挖了一个坑 print(self.__star) #这个坑帮忙实现了一个访问的操作 #外部在使用的时候 p1=People('18','xixi','18',999) p1.get_star() #p1调用外部方法get_star曲线找到隐藏的属性
返回:
私有 earth
earth
(2)接口函数
#类的实现着把内部隐藏起来 class Room: #定义一个Room类完成封装 def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high #定义了一个接口函数 def tell_area(self): #求的是体积 return self.__width * self.__length *self.__high #求体积 r1=Room('卫生间','xixi',100,100,100) #类的使用者想访问长宽高 #arear=r1.__width * r1.__length * r1__high #使用者调用报错 #外面作用调用者可以直接调用接口函数完成求面积 print(r1.tell_area())
返回:
1000000
自省/反射
1.省也称作反射,是由Smith在1982年首次提出的,主要是指程序可以访问,检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快的引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2.在python中提供四个函数可以实现自省
(1)hasattr(object,name):判断对象object中有没有一个name字符串对应的方法或属性,有返回True,没有返回False。
class Salesclerk: #定义一个类salesclerk售货员 feature='beautiful' #数据属性特征beautiful def __init__(self,name,addr): #初始化函数 self.name=name #店名 self.addr=addr #地址 def fruit_sale(self): #定义方法1:卖水果 print('【%s】 正在卖水果' %self.name) def sell_vegetables(self): #定义方法2:卖蔬菜 print('【%s】 正在卖蔬菜' % self.name) b1=Salesclerk('美廉美超市','西城') #实例化一个销售员 print(hasattr(b1,'name')) #hasattr检测b1能不能调用的到name这个属性调用的到返回True print(hasattr(b1,'fruit_sale')) #hasattr检测b1能不能调用的到fruit_sale这个属性调用的到返回True
返回:
True
True
(2)getattr(object,name,default=None):第一个参数传对象object,第二个参数传一个名字,找不到报错
class Salesclerk: #定义一个类salesclerk售货员 feature='beautiful' #数据属性特征beautiful def __init__(self,name,addr): #初始化函数 self.name=name #店名 self.addr=addr #地址 def fruit_sale(self): #定义方法1:卖水果 print('【%s】 正在卖水果' %self.name) def sell_vegetables(self): #定义方法2:卖蔬菜 print('【%s】 正在卖蔬菜' % self.name) b1=Salesclerk('美廉美超市','西城') #实例化一个销售员 print(getattr(b1,'name')) #找到name的数据属性 print(getattr(b1,'fruit_sale')) #找到fruit_sale这个函数的地址 #<bound method Salesclerk.fruit_sale of <__main__.Salesclerk object at 0x01FEB590>> func=getattr(b1,'fruit_sale') #有了这个函数地址可以赋值给func func() #如果没有这个属性会报错,可以把报错内容传到第三个参数里 print(getattr(b1,'fruit_sale77','没有这个属性'))
返回:
美廉美超市
<bound method Salesclerk.fruit_sale of <__main__.Salesclerk object at 0x01D0B590>>
【美廉美超市】 正在卖水果
没有这个属性
(3)setattr(x,y,v):给对象设置属性,第一个参数是对象,第二个参数是属性,第三个参数是属性的值
class Salesclerk: #定义一个类salesclerk售货员 feature='beautiful' #数据属性特征beautiful def __init__(self,name,addr): #初始化函数 self.name=name #店名 self.addr=addr #地址 def fruit_sale(self): #定义方法1:卖水果 print('【%s】 正在卖水果' %self.name) def sell_vegetables(self): #定义方法2:卖蔬菜 print('【%s】 正在卖蔬菜' % self.name) b1=Salesclerk('美廉美超市','西城') #实例化一个销售员 setattr(b1,'sunshine',True) #给b1设置数据属性为sunshine print(b1.__dict__) #打印b1的字典:{'name': '美廉美超市', 'addr': '西城', 'sunshine': True} setattr(b1,'name','沃尔玛超市') #给b1数据属性设置name为沃尔玛超市 print(b1.__dict__) #打印b1的字典:{'name': '沃尔玛超市', 'addr': '西城', 'sunshine': True} setattr(b1,'func',lambda x:x+1) #设定b1函数属性func一调函数完成自加一 print(b1.__dict__) print(b1.func(10)) #调用
返回:
{'name': '美廉美超市', 'addr': '西城', 'sunshine': True}
{'name': '沃尔玛超市', 'addr': '西城', 'sunshine': True}
{'name': '美廉美超市', 'addr': '西城', 'func': <function <lambda> at 0x01D8B660>}
11
(4)delattr(x,y):删除
class Salesclerk: #定义一个类salesclerk售货员 feature='beautiful' #数据属性特征beautiful def __init__(self,name,addr): #初始化函数 self.name=name #店名 self.addr=addr #地址 def fruit_sale(self): #定义方法1:卖水果 print('【%s】 正在卖水果' %self.name) def sell_vegetables(self): #定义方法2:卖蔬菜 print('【%s】 正在卖蔬菜' % self.name) b1=Salesclerk('美廉美超市','西城') #实例化一个销售员 print(b1.__dict__) #打印b1的字典:{'name': '美廉美超市', 'addr': '西城'} #删除 del b1.name #删除b1里的name print(b1.__dict__) #打印b1的字典:{'addr': '西城'}
返回:
{'name': '美廉美超市', 'addr': '西城'}
{'addr': '西城'}
3.为什么用反射,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种后期绑定,即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
class类的三个内置方法
类提供的三个内置方法类的函数属性,obj点的方式去操作属性时会触发(不写没有影响,写了让你的类按照你的逻辑去执行)
(1)__getattr__调用你一个对象不存在的属性的时候才会触发运行
class Foo: #定义一个Foo类 x=1 def __init__(self,y): #定义一个初始化函数 self.y=y def __getattr__(self, item): #定一个__getattr__ print('执行__getattr__') f1=Foo(10) #f1实例化Foo传一个10传给y print(f1.y) #去调y的属性取到10的值 print(getattr(f1,'y')) #同上,len(str),len一个字符串本质就是--->str.__len__(),点__len这个方法 #调用你一个对象不存在的属性的时候才会执行到 __getattr__ f1.aaaa #只有在属性不存在时,会自动触发__getattr__会执行到def __getattr__(self, item):
返回:
10
10
执行__getattr__
(2)__delattr__调用函数属性和数据属性都可以触发删除
class Foo: #定义一个Foo类 x=1 #定义数据属性 def __init__(self,y): #定义一个初始化函数 self.y=y def __delattr__(self, item): #定一个__delattr__ print('删除操作__delattr__') f1=Foo(10) #f1实例化Foo传一个10传给y #调用函数属性和数据属性都可以触发删除 del f1.y #删除属性时会触发 del f1.x #删除属性时会触发
返回:
删除操作__delattr__
删除操作__delattr__
(3)__setattr__实例化初始化的时候会设置属性
class Foo: #定义一个Foo类 x=1 #定义数据属性 def __init__(self,y): #定义一个初始化函数 self.y=y #实例化初始化的时候会设置属性 def __setattr__(self, key, value): #设置属性会触发__setattr__操作, print('__setattr__执行') #self.key = value #设置属性会触发这个操作会进行设置self.key=value,这样就无线递归了,会死循环 self.__dict__[key] = value #给他设置属性最后设置到self自己的属性字典里,本质设置他就是在操作字典,应该使用它,直接操作底层字典方式 f1=Foo(10) #f1实例化Foo传一个10传给y print(f1.__dict__) #再一次设置属性触发一次 f1.a=2 #设置属性的时候会触发__setattr__ print(f1.__dict__)
返回:
__setattr__执行
{'y': 10}
__setattr__执行
{'y': 10, 'a': 2}
举例:
class Foo: #定一个类 def __init__(self,name): #定义一个 __init__方法 self.name=name #定义__getattr__方法,print(f1.age)当找的属性不是name就会触发__getattr__方法 def __getattr__(self, item): #会把f1实例会传给self,age会当作字符串传给item print('你找的属性【%s】不存在' %item) #返回:你找的属性【age】不存在 #定义__setattr__方法,f1=Foo('xixi') 实例化的过程相当于设置属性 def __setattr__(self, k,v): #会把f1传给self,k是('xixi')本质触发了self.name=name执行他的执行前面的name是k后面的name是v print('执行setattr',k,v) #返回:执行setattr name xixi if type(v) is str: #tepe判断v是否是字符串 print('开始设置') #self.k=v #触发__setattr__ self.__dict__[k]=v.upper() #'xixi'是字符串,改成大写 else: print('必须是字符串类型') #定义__delattr__方法,del f1.name触发__delattr__的运行,然后把实例本f1传给self,把属性name传给item def __delattr__(self, item): print('执行__delattr__', item) #print('不允许删除属性【%s】' %item) self.__dict__.pop(item) #删除item就是你的key f1=Foo('xixi') #实例化f1=Foo传一个名字xixi #print(f1.age) #f1.age会触发到__getattr__ #print(f1.name) #f1.name会触发__setattr__设置属性的过程 #print(f1.__dict__) #对比删之前:{'name': 'XIXI'} #del f1.name #del f1.name会触发 __delattr__ #print(f1.__dict__) #删之后:{}
__setitem__,__getitem__,__delitem__跟字典相关的查询,赋值,删除,obj中括号方式去操作属性的时候去触发
class Foo: #定义一个类 def __getitem__(self, item): #查询 print('getitem',item) return self.__dict__[item] #self调用他的属性字典__dict__,[item]找那个属性 def __setitem__(self, key, value): #赋值 print('setitem') self.__dict__[key]=value #self调用他的属性字典__dict__,[key]就是中括号传过来的值name,value是xixi def __delitem__(self, key): #删除 print('delitem') self.__dict__.pop(key) #self调用他的属性字典__dict__,pop(key)指定删除 f1=Foo() #实例化得到f1这个对象 print(f1.__dict__) #查看f1实例字典,没定义init方法f1字典里是空:{} #以字典方式赋值 f1['name']='xixi' #会触发 __setitem__函数运行--->f1.__dict__['name']='xixi' print(f1.__dict__) #查看f1实例字典,赋值成功:'name': 'xixi'} #以字典方式查询: print(f1['name']) #可以调到name的value返回:getitem name 和 xixi #以字典方式删除 del f1['name'] print(f1.__dict__) #返回:delitem 和 {}
{}
setitem
{'name': 'xixi'}
getitem name
xixi
delitem
{}
二次加工标准类型(包装)
二次加工标准触发定制自己的数据类型包装(通过继承和派生的概念,增加修改都是派生一个新的)
包装:python提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,用到了继承/派生(其他的标准类型均可以通过下面的方式进行二次加工)
利用二次加工派生俩个功能一个是获取中间值,一个是新增
class List(list): #定义一个类List继承list #派生一个新的取中间的值:定义一个show_midlle def show_midlle(self): mid_index = int(len(self) / 2) #求自己的长度除以2取整数就是中间的索引 return self[mid_index] # #派生出一个新增加:定义一个append def append(self, p_object): #l2传给self,(18)传给p_object if type(p_object) is str: #判断p_object是否是字符串类型 #self.append(p_object) super().append(p_object) else: print('只能添加字符串类型') #l1=list('ni hao') #print(l1,type(l1)) #就是个列表类型:['n', 'i', ' ', 'h', 'a', 'o'] <class 'list'> l2=List('ni hao') #实例化没写构造函数继承的list #####取中间值 print(l2,type(l2)) #就是个列表类型,type(l2)的类是属于当前文件下的LIST的类<class '__main__.List'>可以寄出标准类型 print(l2.show_midlle()) #取中间的值返回:h #####新增一个字符串 #append(18)传一个不是字符串类型 l2.append(18) #append会触发自己l2自己实例的方法,触发会把(18)参数传到p_object #append('xixi')传一个字符串类型 l2.append('xixi') print(l2) #查看是否加进去了返回:['n', 'i', ' ', 'h', 'a', 'o', 'xixi']
返回:
['n', 'i', ' ', 'h', 'a', 'o'] <class '__main__.List'>
h
只能添加字符串类型
['n', 'i', ' ', 'h', 'a', 'o', 'xixi']
授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些制定,这种做法可以新建,修改或删除原有的产品功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
通过授权方式来实现类型
class FileHandle: #定义一个类FileHandle文件方法 def __init__(self,filename,mode='r',encoding='utf-8'): #操作文件文件名filename,操作模式r读,指定编码,把a.txt传给filname,把w+传给mode #self.filename=filename #打印self.filename返回的是文件名字 self.file=open(filename,mode,encoding=encoding) #open是打开一个文件返回文件描述符(指定filename,mode,encoding=encoding),最后file得到就是文件句柄(self.file里封装了所有文件的方法) self.mode=mode self.encoding=encoding def __getattr__(self, item): #f1.read 触发__getattr__会把f1传给self,read传给item print(item,type(item)) #打印item和它的类型返回:read <class 'str'> self.file.read #self.file属性里有read方法 return getattr(self.file,item) #getattr通过字符串找到自己的属性,自己是self.file #所有实现都通过__getattr__做了中转,文件所有的属性都可以传递过来 f1=FileHandle('a.txt','r+') #实例化指定文件名a.txt传它的模式w+写 #读 f1.read #调f1.read先在f1的字典里去找,找不到read会触发__getattr__(f1.read在调f1的属性) #写 f1.write #调f1.write先在f1的字典里去找,找不到write会触发__getattr__(把f1传给self,把write传给item) f1.write('18181818\n') #执行写入 #seek到0的位置读取 f1.seek(0) print(f1.read()) #返回:18181818
打印结果:
read <class 'str'>
write <class 'str'>
write <class 'str'>
seek <class 'str'>
read <class 'str'>
18181818
通过授权方式来实现类型,给写入到文件的内容加上时间
import time class FileHandle: #定义一个类FileHandle文件方法 def __init__(self,filename,mode='r',encoding='utf-8'): #操作文件文件名filename,操作模式r读,指定编码,把a.txt传给filname,把w+传给mode #self.filename=filename #打印self.filename返回的是文件名字 self.file=open(filename,mode,encoding=encoding) #组合的方式:open是打开一个文件返回文件描述符(指定filename,mode,encoding=encoding),最后file得到就是文件句柄(self.file里封装了所有文件的方法) self.mode=mode self.encoding=encoding #定制自己按时间写入的方法 def write(self,line): #定一个写方法f1.write('18181818\n') 触发的是类定制的方法 t=time.strftime('%Y-%m-%d %X') self.file.write('%s %s' %(t,line)) #定义往文件写内容的操作 def __getattr__(self, item): #f1.read 触发__getattr__会把f1传给self,read传给item print(item,type(item)) #打印item和它的类型返回:read <class 'str'> self.file.read #self.file属性里有read方法 return getattr(self.file,item) #getattr通过字符串找到自己的属性,自己是self.file #所有实现都通过__getattr__做了中转,文件所有的属性都可以传递过来 f1=FileHandle('a.txt','r+') #实例化指定文件名a.txt传它的模式w+写 f1.write('18181818\n') #执行写入 为什么叫授权:通过open(filename,mode,encoding=encoding)组合的方式赋予了文件描述符self.file,利用__getattr__属性去找想要的函数(self.file,item),如果自己不定制,以为的把所有的方法的权限全放开了
面向对象的优点
1.从编程进化论我们得知,面向对象是一种更高等级的结构化编程方式,它的好处就是俩点
(1)通过封装明确了内外,你作为类的缔造者,你是上帝,上帝造物的逻辑方法你无需知道,上帝作为类的造物者是知道类内部的细节,上帝想让你知道的你才知道,这样就明确划分了等级,物就是调用者,上帝就是物的创造者
(2)通过继承+多态在语言层面支持了归一化设计
注意:不用面向对象语言(即不用class),一样可以做归一化,一样可以封装(通过定义模块和接口),只是用面向对象语言可以直接用语言元素显示声明这些而已,而用了面向对象语言,满篇都是class,并不等于就有了归一化的设计,甚至,因为被这些花哨的东西迷惑,反而更加不知道什么才是设计
2.python关于面向对象常用的术语
(1)抽象/实现
抽象出一个类,一类事物共有的数据属性跟动作属性函数属性的一个综合抽象的一个过程,类本身就是抽象的,实现就是实例化
(2)封装/接口
真正的封装是,经过深入的思考,做出良好的抽象,给出完整且最小的接口,并使得内部细节可以对外透明(对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
(3)合成
合成就是把一些小的类组成到一个大的类里面,类和类之间没有共性用组合
(4)派生/继承/继承结构
派生是拿到后自己改了改。继承是继承父类的属性。继承结构是继承顺序
(5)泛化/特化
泛化表示所有的子类与其父类及祖先类有一样的特点
特化描述所有的子类的自定义,也就是什么属性让它与其祖先类不同
(6)多态
指出了对象如何通过他
们共同的属性和动作来操作及访问,而不需考虑他们具体的类
(7)自省/反射