stay hungry stay foolish

python(面向对象高级特性)

slots

python类似js,是一门动态语言,可以动态的给类或者实例添加方法和属性,给类添加的方法和属性可以应用多所有该类的实例。

class Class:
	pass

Class.name='i am class'

obj=Class()

print(obj.name) #i am class

obj.myName='i am obj'

print(obj.myName) #i am obj

def printName(self):
	print(self.name)

Class.printName=printName
#即使在类动态添加方法之前实例化,还是可以调用之后动态添加的方法
obj.printName() #i am class

如果想限制实例可以添加的属性,可以在定义类的时候使用__slots__

class Class:
	__slots__=('name','age')

obj=Class();

obj.name='lisong'

obj.age=18

print(obj.name,obj.name) #lisong 18

obj.title='title' #报错
#__slots__只能限制实例的动态添加属性和方法,类依然能够动态添加
Class.title='class title'

print(obj.title) #class title

@property

@property修饰器可以把类的一个方法变成属性,这样就可以拦截取值操作了,同时这个修饰器还会创造一个以方法名称为后缀的setter修饰器,使用这个修改可以拦截取值操作。@property类似于js中的defineProperty。

class Class:
	@property
	def name(self):
		print('return:'+self._name)
		return self._name
	@name.setter
	def name(self,name):
		print('set:'+name)
		self._name=name
	@property
	def age(self): 
		return 18

obj=Class();

obj.name='lisong' #set:lisong

obj.name #return:lisong

obj.age=20 #报错,没有定义setter,只能读取,不能设置

print(obj.age) #18

多重继承

python天生支持多重继承,而静态语言java只支持单继承。

在类的设计中,如果想让某一个类既有A类的功能,又有B类的功能,只需要让这个类同时继承A类和B类就行了,这种设计通常称之为Mixin。

class Runnable:
	def run(self):
		print('i can run')

class Flyable:
	def fly(self):
		print('i can fly')

class SuperMan(Runnable,Flyable):
	pass

man=SuperMan()

man.run() #i can run

man.fly() #i can fly

定制类

在类中重定义形如__xxx__的方法,可以让类的实例有特殊的行为。

len

class Class:
	def __len__(self):
		return 10

len(Class()) #10

str

class Class:
	def __str__(self):
		return 'i am class'
#默认__str__()打印出内存地址,可以重定义
print(Class()) #i am class

iter

如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

class Class:
	def __iter__(self):
		return (x*x for x in [1,2,3])

for x in Class():
	print(x) #1 2 3

class Class:
	sum=0
	def __iter__(self):
		return self
	def __next__(self):
		self.sum+=1
		if(self.sum>3):
			raise StopIteration
		else:
			return self.sum

for x in Class():
	print(x) #1 2 3

getitem

使实例可以作用于下标操作符。

class Class:
	arr=[1,2,3,4,5,6]
	def __getitem__(self,n):
		if(isinstance(n,int)):
			return self.arr[n]
		if(isinstance(n,slice)):
			start=n.start
			stop=n.stop
			return self.arr[start:stop]

obj=Class()
print(obj[2]) #3
print(obj[1:3]) #2,3

getattr

当属性或方法不存在时会调用该方法。

class Class:
	def __getattr__(self,attr):
		if(attr=='name'):
			return 'lisong'
		if(attr=='age'):
			return lambda:18

obj=Class()
obj.name #lisong
obj.age() #18

call

如果定义了__call__方法,实例可以当做方法调用。

class Class:
	def __call__(self):
		print('run __call__')


obj=Class()
obj() #run __call__

枚举

枚举对象自动赋给成员int常量,默认从1开始计数

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

for name, member in Month.__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

通过继承Enum,可以自定义枚举成员的值

from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

print(Weekday.Mon) #1 
Weekday.Mon=10 #报错,不可以重新赋值
for name, member in Weekday.__members__.items():
    print(name, '=>', member, ',', member.value)
#Sun => Weekday.Sun , 0
#Mon => Weekday.Mon , 1
#Tue => Weekday.Tue , 2
#Wed => Weekday.Wed , 3
#Thu => Weekday.Thu , 4
#Fri => Weekday.Fri , 5
#Sat => Weekday.Sat , 6

动态创建类

type方法

type动态创建类需要三个参数

  • class的名称;
  • 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  • class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
def hello(self):
	print('hello world')

Hello=type('Hello',(object,),{'hello':hello})

Hello().hello() #hello world

metaclass

metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

当定义一个类的时候,传递一个metaclass关键字参数,则定义了当创建该类的时候需要使用该关键字所指向的类的__new__方法来创建。

__new__()方法接收到的参数依次是:

  • 当前准备创建的类;
  • 类的名字;
  • 类继承的父类集合;
  • 类的方法、属性集合。
class superMetaclass(type):
	def __new__(obj,name,bases,attrs):
		print(obj) #<class '__main__.superMetaclass'>
		attrs['test'] = lambda self:'this is a test'
		return type.__new__(obj,name,bases,attrs)

class Class(metaclass=superMetaclass):
	def run(self):
		print('running')

obj=Class()
obj.test() #this is a test
obj.run() #running