python学习(七)面向对象高级编程

使用__slots__

  • 通常方法直接定义在class中,动态绑定允许程序运行过程中动态给class加上功能。

使用__slots__

  • 定义class的时候,定义一个特殊的__slots__变量,限制该class实例能添加的属性。
class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
  • __slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。
  • 子类实例允许定义的属性就是自身__slots__加上父类__slots__

使用@property

  • @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


s = Student()
s.score = 60 # OK,实际转化为s.set_score(60)
s.score # OK,实际转化为s.get_score()
  • 定义只读属性:只定义getter方法,不定义setter方法
# birth为可读写属性;age为只读属性
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
  • 属性的方法名不要和实例变量重名

多重继承

  • 一个子类可同时获得多个父类的所有功能。

MixIn

  • 设计类的继承关系,通常主线都是单一继承。
  • 需要“混入”额外功能,通过多重继承实现。
  • 给一个类增加多个功能,设计类时,优先考虑通过多重继承组合多个MixIn功能。
# 多进程模式的TCP服务
class MyTCPServer(TCPServer, ForkingMixIn):
    pass
# 多线程模式的UDP服务
class MyUDPServer(UDPServer, ThreadingMixIn):
    pass
# 协程模型
class MyTCPServer(TCPServer, CoroutineMixIn):
    pass

定制类

  • __xxx__特殊用途的函数,可以帮助我们定制类。

__str__

  • 定义__str__()方法,返回字符串,用于print()。
  • 直接显示变量调用的是__repr__(),返回程序开发者看到的字符串,服务调试。
class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

__iter__

  • 定义__iter__()方法,返回迭代对象,用于for ... in循环。
  • __next__()方法拿到循环的下一个值,遇到StopIteration错误时退出循环。
# 斐波那契数列
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__

  • 定义__getitem__()方法,可实现按照下标取出元素
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

# 实现切片方法
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
  • 如果把对象看成dict__getitem__()的参数也可能是一个可以作keyobject,例如str
  • __setitem__()方法,把对象视作listdict来对集合赋值。
  • __delitem__()方法,用于删除某个元素。

__getattr__

  • __getattr__()方法,动态返回一个属性。
  • 当调用不存在的属性时,会试图调用__getattr__()来尝试获得属性。返回函数也可以。
# 返回属性
class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
# 返回函数
class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
# 只响应特定属性,其他抛出AttributeError的错误
class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
  • 可以把一个类的所有属性和方法调用全部动态化处理,不需要任何特殊手段。
  • 完全动态调用的特性可以针对完全动态的情况作调用。
# 利用完全动态的__getattr__,写出一个链式调用
# 针对REST API写SDK
# 无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变
class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__

Chain().status.user.timeline.list # '/status/user/timeline/list'

__call__

  • 任何类,只需要定义一个__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() # self参数不要传入
  • __call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样。
  • callable()可以判断一个对象是否是“可调用”对象。

使用枚举类

  • 为枚举类型定义一个class类型,每个常量都是class的一个唯一实例。Enum类可实现。
from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 直接使用
Month.Jan
# 枚举所有成员
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)
# value属性则是自动赋给成员的int常量,默认从1开始计数
# 更精确地控制枚举类型,从Enum派生自定义类
from enum import Enum, unique

@unique # @unique装饰器保证没有重复值。
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

# 调用
print(Weekday.Tue) # Weekday.Tue
print(Weekday['Tue']) # Weekday.Tue
print(Weekday.Tue.value) # 2
Weekday(1) # Weekday.Mon

使用元类

type()

  • 函数和类的定义,动态语言运行时动态创建的;静态语言编译时定义。
  • type()查看一个类型或变量的类型,类类型就是type,实例类型就是class 类名
  • type()既可以返回一个对象的类型,又可以创建出新的类型。
def fn(self, name='world'): # 先定义函数
     print('Hello, %s.' % name)

Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
  • type()创建class对象依次传入:class的名称;继承的父类集合;方法绑定函数。
  • type()允许动态创建类,动态语言支持运行期动态创建类。

metaclass

  • metaclass控制类的创建行为。先定义metaclass(元类),然后创建类,最后创建实例。
# 给自定义MyList增加add方法。
# metaclass是类的模板,所以必须从`type`类型派生。
# 默认习惯,metaclass的类名总是以Metaclass结尾。
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)
# 定义类时使用ListMetaclass来定制类,传入关键字参数metaclass.
# 创建MyList时,要通过ListMetaclass.__new__()来创建
class MyList(list, metaclass=ListMetaclass):
    pass
  • __new__()方法接收参数:准备创建的类对象;类名;父类集合;类的方法集合。

  • ORM(Object Relational Mapping),对象-关系映射。

  • 关系数据库的一行映射为一个对象,一个类对应一个表。

# 编写一个ORM框架,所有的类都只能动态定义。
# 使用者才能根据表的结构定义出对应的类来。

# 预设调用接口
class User(Model):
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

# 创建一个实例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()

# 按接口实现ORM
# 保存数据库表的字段名和字段类型
class Field(object):

    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)
# 定义各种类型的Field,比如StringField,IntegerField等
class StringField(Field):

    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')

class IntegerField(Field):

    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')
# 编写元类
'''
1.排除掉对Model类的修改;
2.在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个__mappings__的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);
3.把表名保存到__table__中,这里简化为表名默认为类名。
'''
class ModelMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        attrs['__table__'] = name # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)
# 基类
class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值