在第一篇文中,记录了部分的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属性,绑定其他属性将会报错。

协程

协程的特点在于是一个线程执行,那和多线程比,协程有何优势?

最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

整个流程无锁,由一个线程执行,produceconsumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

Python对协程的支持是通过generator实现的。

async/await

asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异步操作。

为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法asyncawait,可以让coroutine的代码更简洁易读。

async def hello():
    print("Hello world!")
    r = await asyncio.sleep(1)
    print("Hello again!")
如果觉得我的文章对你有用,请随意赞赏