共计 8037 个字符,预计需要花费 21 分钟才能阅读完成。
引言
这篇文章介绍 Python 如何将函数与数据整合成类,并通过一个对象名称来拜访它们。
文章目录
0×9. 类的多重继承
0×10. 定制类
0×11. 枚举类
0×12. 动静类
0×9. 类的多重继承
python 中的类提供了多重继承的性能,比方 C 类能够继承 B 类同时又能继承 A 类,C 类可能应用 B 和 A 类所有属性和办法,请看上面的实例:
#!/usr/bin/env python
#coding=utf-8
########
class A(object):
def _Print_website(self):
print("www.qingsword.com")
########
class B(object):
def _Print_HelloWorld(self):
print("Hello World")
########
#多重继承只须要在子类括号中用逗号分隔须要继承的类即可
class C(A,B):
def __init__(self):
self.name="qingsword"
#因为 C 类同时继承了 A 和 B 类,所以它的实例能够应用 A 和 B 类所有办法和属性
x=C()
x._Print_website()
x._Print_HelloWorld()
print(x.name)
#程序输入
www.qingsword.com
Hello World
qingsword
0×10. 定制类
当咱们实例化一个类对象后,应用 print 打印这个类对象,往往会打印出一个类名称和内存地址,请看上面的实例:
#!/usr/bin/env python
#coding=utf-8
########
class A:
#--------
def __init__(self,name="qingsword"):
self.name=name
x=A("www.qingsword.com")
print(x)
#程序输入
<__main__.A object at 0x7f41d6039f28>
为了让打印输出更加的敌对,python 提供了两个函数__str__和__repr__,前者能够让 print 打印一个对象时,输入这个函数下定制的返回信息;后者是在调试过程中让调试程序(IDLE 提示符环境等)在间接输出对象时,可能打印出咱们定制的信息,这两者通常写入雷同的返回信息,例如,将下面的实例更改如下:
#!/usr/bin/env python
#coding=utf-8
########
class A:
#--------
def __init__(self,name="qingsword"):
self.name=name
#--------
def __str__(self):
"""定制对象返回信息"""
return "Class A Object self.name=%s"%self.name
#间接让 repr 函数等于 str
__repr__=__str__
x=A("www.qingsword.com")
print(x)
#程序输入
Class A Object self.name=www.qingsword.com
除了定制对象的输入信息外,如果一个类要用作 for 循环,Python 提供了一个__iter__办法,该办法返回一个迭代对象,可应用 for 循环迭代这个对象,for 会一直调用该对象的__next__()办法拿到循环的下一个值,直到遇到 StopIteration 谬误(或 break)退出循环,能够将一个类定制成可迭代类,请看上面的实例:
#!/usr/bin/env python
#coding=utf-8
########
class Fib(object):
#--------
def __init__(self):
self.a,self.b=0,1
#实例自身就是迭代对象,所以 iter 函数返回本人
def __iter__(self):
return self
#如果对象中的 a 属性值大于 20,就进行循环,否则返回每次循环中失去的 a 值
def __next__(self):
self.a,self.b=self.b,self.a+self.b
if self.a>20:
raise StopIteration()
return self.a
x=Fib()
for y in x:
print(y)
#这是一个斐波拉契迭代类,程序输入如下
1
1
2
3
5
8
13
#如果删除类中的 self.a>20 的判断,能够将 for 循环更改成
for y in x:
#能够手动指定一个迭代范畴
if y<20:
print(y)
else:
break
下面的可迭代类看起来更像是一个生成器,但当初咱们还无奈像列表那样应用一个索引来获得单个值,为此 python 提供了一个__getitem__办法,用于实现获得单个值的类,批改下面的程序如下:
#!/usr/bin/env python
#coding=utf-8
########
class Fib(object):
#依据输出的 n,返回 x 的值
def __getitem__(self,n):
#x 和 y 只是函数中的一个局部变量,每次调用函数,都会被从新设置成 1
x,y=1,1
while n>0:
x,y=y,x+y
n-=1
return x
x=Fib()
print(x[2])
print(x[12])
#程序输入
2
233
如果想要给这个类增加一个切片性能,能够批改下面的代码如下:
#!/usr/bin/env python
#coding=utf-8
########
class Fib(object):
def __getitem__(self,n):
#判断传入的是一个分片类型还是一个索引值
if isinstance(n,int):
x,y=1,1
while n>0:
x,y=y,x+y
n-=1
return x
elif isinstance(n,slice):
#获取分片的起始值
start=n.start
stop=n.stop
if start is None:
start=0
x,y=1,1
L=[]
for a in range(stop):
if a>=start:
L.append(x)
x,y=y,x+y
return L
x=Fib()
print(x[12])
print(x[:12])
print(x[6:12])
#程序输入
233
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
[13, 21, 34, 55, 89, 144]
下面这个简略的分片器并没有对正数和分片步长做解决,仅仅是提供一个思路,通知大家,通过这种定制函数,能够将一个类定制成任何数据类型,能够是列表字典元组等等。
通常状况下,咱们调用一个对象的某个属性和办法时,如果这个属性和办法不存在,就会报错,如下所示:
#!/usr/bin/env python
#coding=utf-8
########
class A(object):
def __init__(self):
self._path=""
x=A()
#调用_path 没有问题
print(x._path)
#但调用一个不存在的 sub 属性就会报错,提醒 sub 属性不存在
print(x.sub)
python 提供了一个__getattr__函数,它接管一个不存在的属性或办法名称,而后返回一个咱们设定的值,如果咱们没有定义返回值,它将返回一个 None,这比间接抛出一个谬误要敌对的多,请看上面的实例:
#!/usr/bin/env python
#coding=utf-8
########
class A(object):
def __init__(self):
self._path=""
def __getattr__(self,attr):
if attr=="blog":
return "www.qingsword.com"
elif attr=="doit":
return "ok"
#一个函数返回值
elif attr=="age":
return lambda :23
x=A()
print(x.blog)
print(x.doit)
print(x.age())
print(x.nothing)
#程序输入
www.qingsword.com
ok
23
None
请留神__getattr__函数只有当类中没有定义这些属性值时才接管他们,并且依据咱们本人的判断返回值,如果类中有这些属性或办法,则会优先应用它们,另外,如果这个函数接管到了一个没有定义返回值的名称,默认返回 None,除非手动抛出一个 AttributeError,比方,在 if 的开端增加上面的代码,再次运行就会抛出一个异样,提醒 nothing 不存在:
else:
raise AttributeError("谬误的属性或办法名:%s"%attr)
__getattr__最常见的利用就是链式调用,请看上面的实例:
#!/usr/bin/env python
#coding=utf-8
########
class A(object):
def __init__(self,path=""):
self._path=path
print('self._path is %s' % self._path)
def __getattr__(self,attr):
return A("%s/%s"%(self._path,attr))
def __str__(self):
return self._path
__repr__=__str__
x=A()
print(x.www.qingsword.com)
#程序输入
#x=A()初始化时的输入
self._path is
#x.www 时因为没有 www 属性,调用了__getattr__函数,并且传递了一个 www,而后递归调用 A,这一步相当于 A("/www")
self._path is /www
#x.www.qingsword 会再次激活__getattr__函数,因为 www 并没有 qingsword 属性,以此类推
self._path is /www/qingsword
self._path is /www/qingsword/com
/www/qingsword/com
咱们在实例化一个对象的时候,能够通过 ” 对象名. 属性名 ” 来应用其中的属性,或者通过 ” 对象名. 办法名()” 来调用其中的办法,除此之外,python 提供了一个__call__函数,这个函数容许对象调用本身,请看上面的实例:
#!/usr/bin/env python
#coding=utf-8
########
class A(object):
def __init__(self,name="qingsword"):
self.name=name
def __call__(self):
print("My name is %s"%self.name)
x=A()
#间接调用对象本身,就相当于执行了对象内的__call__函数,就如同 x 自身是一个函数而不是类对象一样
x() #输入 "My name is qingsword"
#咱们能够通过 callable()函数来判断一个对象是否可调用,这个函数返回一个布尔值,如果咱们正文掉 A 类中的 call 函数,那么上面的第一句就会返回 False,大家能够尝试一下
print(callable(x)) #True
print(callable(str)) #True
print(callable([1,2,3])) #False
0×11. 枚举类
Python3.0 之后,提供了一个枚举类型,补救了 Python2 没有枚举的有余,首先来看一个简略的枚举实例:
#!/usr/bin/env python
#coding=utf-8
#导入枚举模块
from enum import Enum
#定义一个枚举变量 m
m = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
#应用不同的办法获取枚举中的名称和值,和列表不同,枚举的索引是从 1 开始的
print(m(1)) #Month.Jan
print(m.Mar) #Month.Mar
print(m["Oct"]) #Month.Oct
print(m.Mar.value) #3
#打印出枚举中所有的名称
x=len(m.__members__) #取得枚举个数
for i in range(1,x+1):
print(m(i))
#程序输入
Month.Jan
Month.Feb
Month.Mar
Month.Apr
Month.May
Month.Jun
Month.Jul
Month.Aug
Month.Sep
Month.Oct
Month.Nov
Month.Dec
#获取枚举成员名称和全名,m.__members__.items()会将枚举宰割成一个列表,每个元素都为一个元组,每个元组中又蕴含一个字符串和一个字典类型,宰割后的列表大略是这个样子[("Jan",<Month.Jan:1>),("Feb",<Month.Feb:2>)....],而后应用 name 接管第一个参数(Jan),member 接管第二个参数(<Month.Jan:1>),member 能够拜访键,用 member.value 能够拜访值
for name, member in m.__members__.items():
print(name,'=>',member,',',member.value)
#程序输入
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
理解了枚举的创立和拜访办法后,再来看看枚举类的创立办法:
#!/usr/bin/env python
#coding=utf-8
from enum import Enum,unique
@unique #unique 装璜器能够确保枚举中没有反复值
class Week(Enum):
"""继承枚举类,能够指定每个名称的值"""
Sun=0 #将起始索引设定成 0
Mon=1
Tue=2
Wed=3
Thu=4
Fri=5
Sat=6
#拜访办法同枚举
print(Week(0))
print(Week.Sat.value)
print(Week["Thu"])
#程序输入
Week.Sun
6
Week.Thu
在 Python3 中,枚举类常常用于定义常量,因为枚举中每个名称对应的值是不可变的,例如:
#!/usr/bin/env python
#coding=utf-8
from enum import Enum,unique
@unique
class const(Enum):
"""常量枚举类"""
website="www.qingsword.com"
author="qingsword"
pi=3.1415
print(const.website.value)
print(const("qingsword"))
print(const["pi"].value)
#程序输入
www.qingsword.com
const.author
3.1415
0×12. 动静类
动静语言和动态语言在创立类的时候也有所不同,动静语言类的创立是在程序执行后才顺次执行类中的语句,动态化创立一个类,而动态语言是在编译时就曾经创立了这些类;咱们在应用 type 判断一个类的类型时,往往看到类的类型是 type,这又与动静类的创立有什么关系呢?请看上面的实例:
#!/usr/bin/env python
#coding=utf-8
########
class A(object):
def Website(self):
print("www.qingsword.com")
x=A()
print(type(A))
print(type(x))
#程序输入,从输入中能够看到,x 是 A 类的一个实例,但 A 类自身却是一个 class type
<class 'type'>
<class '__main__.A'>
实际上 type 办法不仅能够判断对象的类型,python 还能够应用它来动态创建类,而应用 type 动态创建的类与间接应用 class 创立的类成果是一样的,这就是为什么下面 type 判断 A 类的类型是 class type 的起因,实际上两者能够看做同一种类型,上面的实例演示了如何通过 type 动态创建一个类:
#!/usr/bin/env python
#coding=utf-8
def Website(self):
print("www.qingsword.com")
def SayHello(self,name="qingsword"):
print("Hello %s"%name)
#B 指向一个 Type 动态创建的类,所以 B 就代表这个类 (A) 自身
#type 的语法:#type("类名",(继承类列表,...),dict(类办法名 = 绑定的办法名,...))
#应用这种办法咱们就能够动静的将内部的办法绑定到一个类中,从而在运行时创立一个类
B=type("A",(object,),dict(web=Website,hello=SayHello))
x=B()
print(x)
print(type(B))
x.web()
x.hello("xxx")
#程序输入
<__main__.A object at 0x7f7d70821048>
<class 'type'>
www.qingsword.com
Hello xxx