在第一篇文中,记录了部分的python的基础语法,本文继续。
面向对象
类和类中的方法
类定义时,类名后的括号内写基础的父类名称,如果没有合适的继承父类可写,要是用object类(感觉这有点多此一举)
class Student(object):
pass
为了避免产生过多的中间类,可以使用mixin
机制,如下:
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__
方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
对于类的私有变量,可以使用两个下划线前缀来表示:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def set_score(self, score):
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
上面定义一个属性的set和get方法有太多模版代码,Python内置的@property
装饰器就是负责把一个方法变成属性调用的:
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
把一个getter方法变成属性,只需要加上@property
就可以了,此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性,下面的birth
是可读写属性,而age
就是一个只读属性,因为age
可以根据birth
和当前时间计算出来:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2015 - self._birth
对于双下划线开头,且双下划线结尾的,是可以直接访问的,属于特殊变量。
python的动态特性
python这种动态类型语言,允许我们在运行时动态改变类和对象的功能,比如动态的给类和对象增加属性或者方法。
动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。也就是如果某个函数需要A类型的对象,我们不一定要有一个继承自A的子类B,只要这个传入的对象有A类里的方法,那么这个对象就可以认为是看起来像A类型,这就是python这类动态语言区别于静态语言的地方。
同时由于Python是动态语言,根据类创建的实例可以任意绑定属性。
给实例绑定属性的方法是通过实例变量,或者通过self
变量:
class Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
但是,如果Student
类本身需要绑定一个属性呢?可以直接在class中定义属性,这种属性是类属性,归Student
类所有:
class Student(object):
name = 'Student'
当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。
实例属性值会覆盖类的属性值,可以删除一个实例的属性值,这时候会使用类的属性值
使用__slots__
通过在类中定义添加一个__slots__
属性,指定该class对象能添加的属性只能有哪些,来达到限制的目的:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
则这时候只能对Student对象动态绑定name和age属性,绑定其他属性将会报错。
协程
协程的特点在于是一个线程执行,那和多线程比,协程有何优势?
最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
整个流程无锁,由一个线程执行,produce
和consumer
协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
Python对协程的支持是通过generator实现的。
async/await
用asyncio
提供的@asyncio.coroutine
可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from
调用另一个coroutine实现异步操作。
为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async
和await
,可以让coroutine的代码更简洁易读。
async def hello():
print("Hello world!")
r = await asyncio.sleep(1)
print("Hello again!")