面向对象高级编程

来源:互联网 发布:网络机顶盒怎么看直播 编辑:程序博客网 时间:2024/04/29 17:00

面向对象高级编程

使用__slots__

当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性

class Student(object):     pass

尝试给实例绑定一个属性:

class Student(object):    passs = Student()s.name = "csx"print s.name# 输出:csx

还可以尝试给实例绑定一个方法:

# coding=utf-8class Student(object):    passs = Student()s.name = "csx"print s.name# 定义一个方法作为实例方法def set_age(self, age):    self.age = agefrom types import MethodType# 给实例绑定一个方法s.set_age = MethodType(set_age, s, Student)s.set_age(25)print s.age

但是,给一个实例绑定的方法,对另一个实例是不起作用的:

为了给所有实例都绑定方法,可以给class绑定方法:

Student.set_score = MethodType(set_score, None, Student)
  • 使用__slots__

如果我们想要限制class的属性怎么办?比如,只允许对Student实例添加name和age属性。

为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class能添加的属性:

# coding=utf-8class student(object):    __slots__ = ("name", "age")s = student()s.name = "csx"s.age = 13s.score = 90"""Traceback (most recent call last):  File "G:/python/day2/slot.py", line 9, in <module>    s.score = 90AttributeError: 'student' object has no attribute 'score'"""

使用@property

封装属性时,同时进行参数检查

# coding=utf-8class student(object):    def get_score(self):        return self._score    def set_score(self,score):        if not isinstance(score,int):            raise ValueError('score must be an integer!')        if score < 0 or score > 100:            raise ValueError('score must between 0 ~ 100!')        self._score = scores = student()s.set_score(70)print s.get_score()# 输出:70s.set_score("csx")print s.get_score()"""Traceback (most recent call last):  File "G:/python/day2/slot.py", line 16, in <module>    s.set_score("csx")  File "G:/python/day2/slot.py", line 7, in set_score    raise ValueError('score must be an integer!')ValueError: score must be an integer!"""

上面的调用方法又略显复杂,没有直接用属性这么直接简单。

还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。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 = values = Student()s.score = 90print s.score# 输出:90

还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:

多重继承

实例

class Mammal(Animal):    passclass Runnable(object):    def run(self):        print('Running...')class Flyable(object):    def fly(self):        print('Flying...')        # 多重继承class Dog(Mammal, Runnable):    pass

Mixin

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为Mixin。

Mixin的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系。

定制类

__str__

类似java中的toString,输出默认的打印信息

__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

__iter__

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

我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

class Fib(object):    def __init__(self):        self.a, self.b = 0, 1 # 初始化两个计数器a,b    def __iter__(self):        return self # 实例本身就是迭代对象,故返回自己    def next(self):        self.a, self.b = self.b, self.a + self.b # 计算下一个值        if self.a > 100000: # 退出循环的条件            raise StopIteration();        return self.a # 返回下一个值

__getitem__

像list那样按照下标取出元素,需要实现__getitem__()方法:

class Fib(object):    def __getitem__(self, n):        a, b = 1, 1        for x in range(n):            a, b = b, a + b        return a

__getattr__

解决,调用不存在属性时,调用出错的问题

class Student(object):    def __init__(self):        self.name = 'Michael'    def __getattr__(self, attr):        if attr=='score':            return 99s = Student()s.score# 输出:99

当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,

__call__

一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?类似instance()?在Python中,答案是肯定的。

任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。请看示例:

class Student(object):    def __init__(self, name):        self.name = name    def __call__(self):        print('My name is %s.' % self.name)s = Student('Michael')s()# 输出:My name is Michael.

call()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

使用元类

type()动态创建类

type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:

def fn(self, name='world'): # 先定义函数     print('Hello, %s.' % name)Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
  • 要创建一个class对象,type()函数依次传入3个参数:
    1. class的名称;
    2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

metaclass

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

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

metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。

我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:

定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

# metaclass是创建类,所以必须从`type`类型派生:class ListMetaclass(type):    def __new__(cls, name, bases, attrs):        attrs['add'] = lambda self, value: self.append(value)        return type.__new__(cls, name, bases, attrs)class MyList(list):    __metaclass__ = ListMetaclass # 指示使用ListMetaclass来定制类
  • 当我们写下__metaclass__ = ListMetaclass语句时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.new()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

    • __new__()方法接收到的参数依次是:
    • 当前准备创建的类的对象;
    • 类的名字;
    • 类继承的父类集合;
    • 类的方法集合。
0 0
原创粉丝点击