面对对象高级编程
对于python这种动态语言,我们可以在创建了class实例之后,给其绑定属性甚至绑定方法
class My_TestClass(object):
"""docstring for My_TestClass"""
pass
test1=My_TestClass()
test1.name="liyuyuan" # 给实例绑定对象
def set_name(self, name):
self.name=name
from types import MethodType
test1.set_name=MethodType(set_name, test1) #实例对象绑定方法
test1.set_name("lyy")
print(test1.name) # lyy
给一个实例对象绑定的方法,对另外的实例对象是不起作用的,应该给
class
绑定方法
动态语言允许我们在程序运行过程中动态给class
增加功能
My_TestClass.set_name=set_name
test2=My_TestClass()
test2.set_name("wtt")
print(test2.name) # wtt
slots
通过__slots__
来对实例的属性进行限制能添加的属性
class My_Slots_Class(object):
"""docstring for My_Slots_Class"""
__slots__=("name", "score")
# def __init__(self, age): #不能有 name、score外的其他属性
# self.age=age
pass
slot1=My_Slots_Class()
slot1.age=10 #AttributeError: 'My_Slots_Class' object has no attribute 'age'
__slots__
定义属性仅对当前类实例起作用,对继承的子类是不起作用的,可以在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
。
class My_Slots_Subclass(My_Slots_Class):
"""docstring for My_Slots_Subclass"""
__slots__=("age")
pass
#此时允许的属性为子类的__slots__加上父类的__slots__
slot2=My_Slots_Subclass()
slot2.age=22
slot2.ttt=10 #AttributeError: 'My_Slots_Subclass' object has no attribute 'ttt'
@property
python内置的@property
装饰器就负责把一个方法变为属性调用
class My_Property_Class(object):
@property
def age(self):
print("获取age",self._age)
return self._age
@age.setter
def age(self, age):
#可以在此处检查传入参数处理
print("设置age",age)
self._age=age
property1=My_Property_Class()
property1.age=10 #设置age 10
property1.age # 获取age 10
如果我们只定义@property
那么该属性就是只读的
多继承
python中支持多继承,一个子类可以同时获得多个父类的所有功能
MixIn
一般设计为单继承,但是当需要额外功能时可以用过多继承实现,这就是
MixIn
定制类
类似__slots__
限制类的属性
__len__()
方法让class能作用域len()
函数
str
__str__()
对于打印出来的对象信息修改
__repr__()
对程序员看到的对象信息修改
class Student(object):
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
__repr__ = __str__ #经常把两个写作相同
...
print(Student('Michael')) #Student object (name: Michael)
#当开发者在命令行直接输入对象 会打印对象信息,这是调用的是__repr__()
iter
如果类需要被for...in
循环,必须实现__iter__()
方法,返回一个可迭代对象,然后调用对象的__next__()
方法获取循环下一个值,直到遇到StopIteration
错误退出循环
学习迭代器一章知道,调用iter()方法转为迭代器,然后调用next方法获取下一个值,其内部实现就是
__iter__()
和__next__()
class My_Iter_Class(object):
"""docstring for My_Iter_Class"""
def __init__(self):
self.a=0
def __iter__(self):
return self
def __next__(self):
self.a+=1
while self.a>10:
raise StopIteration()
print(self.a)
for x in My_Iter_Class():
pass
getitem
对于实现了__iter__()
和__next__()
可以用for遍历,但是并不能像list一样取某个下标或者进行切片操作,这时需要实现__getitem__()
方法
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
#判断是否为切片对象
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
在
__getitem__()
方法中,需要判断是取下标索引传入参数为int
,还是传入的为切片slice
进行切片操作
上面对切片操作并不完善,比如对于负数的处理
对应于__getitem__()还有__setitem__()来对类进行赋值 delitem()删除某个元素
getattr
当我们调用类中不存在的属性或方法时时,Python解释器会试图调用__getattr__(self, 'attr')
来获得属性或方法,我们就可以通过这个方法来返回对应不存在属性值或方法
如果我们实现了这个方法,默认返回是
None
无法提示用户该属性不存在,我们可以返回一个raise AttributeError
错误
call
当我们在类内部实现__call__()
方法后,我们就可以直接对实例进行调用
class My_Call_Class(object):
def __call__(self):
print("调用自身call")
obj1=My_Call_Class()
obj1() # 调用自身call
#判断一个对象是否可调用
if callable(obj1): #True
print("可调用") #可调用
对于可直接调用对象,可以将其看做函数,可以传参数
枚举类
Python为定义大量常量的枚举 提供了Enum
类
使用Enum实现一个简单的枚举
Month = Enum("Month", ("Jan", "Feb", "Mar", "Apr"))
print(Month.Jan) # Month.Jan
print(Month.Mar.value) # 3
# __members__返回所有成员的name->value的map
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
对于某些情况我们可能需要自定义枚举,可以从Enum
派生出自定义类
class WeekDay(Enum):
Sun=7
Mon=1
Tue=2
Wed=3
Thu=4
Fri=5
Sat=6
print(WeekDay.Sun) #WeekDay.Sun
print(WeekDay.Fri.value) #5
for name, member in WeekDay.__members__.items():
print(name, '=>', member, ',', member.value)
Enum
可以把相关常量定义在一个不可变class
中,成员之间可以直接比较
元类
动态语言和静态语言最大的不同就是,函数和类的定义不是编译时定义的而是运行时动态创建的
当python解释器载入模块时,就会依次执行该模块中的所有语句
class
的定义是在运行时动态创建的,创建class
的方法就是使用type()
函数
利用type
函数创建一个class
def hello_func(self, name="world"):
print("hello, ", name)
#用type创建Hello class
Hello_Class=type("Hello", (object,), dict(hello=hello_func))
h1= Hello_Class()
h1.hello() # hello, world
用type定义的类与class定义的完全相同,python解释器遇到class定义类也是仅仅扫描一下后调用type()
方法来定义类
当前执行的模块名字为__main__
metaclass
metaclass
允许我们创建或者修改类
metaclass
是类的模板,所以必须从type
类型派生
该节不常用,自行参考廖雪峰的python学习元类