理解python面向对象编程

python类使用class关键字定义,使用class定义的类又可以称为类型对象

也可以说,使用class关键字可以创建类型对象。

继承了object对象的类型对象是New-Style Class

在python3中所有类型对象都是New-Style Class

class User(object): pass

类型对象的结构体

object类的C结构体如下:

    typedef struct
    {
        PyObject_HEAD PyObject *cl_bases;    PyObject *cl_dict;    PyObject *cl_name;
        PyObject *cl_getattr;
        PyObject *cl_setattr;
        PyObject *cl_delattr;
    } PyClassObject;

从上面的结构体可以看出,object类存储了所有的静态字段和方法

通过实例化object类可以得到【实例对象】

In [76]: class User(object): pass
# 实例化出实例对象u
In [77]: u = User()

# 由此可以看出u对象的ob_type是User对象
In [78]: type(u)
Out[78]: __main__.User

【实例对象】C结构体定义如下:

typedef struct
{
    PyObject_HEAD
    PyClassObject *in_class;
    PyObject  *in_dict;
    PyObject  *in_weakreflist;
} PyInstanceObject;

实例对象也有一个dict字段,保存的是所有的实例字段

In [79]: u.name = "sunli"
In [81]: u.__dict__
Out[81]: {'name': 'sunli'}

需要清楚实例字段和静态字段的区别,类对象的字段的查找顺序是:

instance.__dict__ -> class.__dict__ -> baseclass.__dict__

字段初始化

class User(object):
    # 静态字段
    __table__ = "user"

    # 初始化
    def __init__(self, name):
        self.name = name

    # 序列化
    def __str__(self):
        return "my name is: {0}".format(self.name)

小心得,可以看到__init__, __str__其实是重写type对象里面__init__, __str__ 函数

还记得type对象定义的操作函数有哪些么

类方法

实例方法和函数的区别是self 这个隐式参数

# 定义一个打印方法
def p(self): print(self.name)

# 给实例对象动态绑定一个方法
u.p = p

# 调用p方法
u.p(u)

使用 staticmethod、classmethod 装饰器可以构造静态方法,静态方法可以被类型对象直接调用

>>> class User(object):
...     def a(): pass
...
...     @staticmethod 没有参数
...     def b(): pass
...
...     @classmethod  有参数
...     def c(cls): pass
>>> User.a

类继承

和其他语言不同的是,类继承需要显示的调用父类的初始化函数

# 继承了BaseMenu类
class Menu(BaseMenu):

    def __init__(self, restaurant_id):
        # 必须显示的调用父类的init函数
        super(Menu, self).__init__(restaurant_id)

super 的类型参数决定了在类 mro 列表中的搜索起始位置

# 表示调用父类的__init_函数,查找的顺序是Menu类的父类
super(Menu, self).__init__(restaurant_id)

如果需要显示的调用父类的函数

class TargetUrlValidator(StringValidator):
    @classmethod
    def validate(cls, target_url):
        # 显式调用父类函数
        super(TargetUrlValidator, cls).validate(target_url)

__mro__: 关键字记录了类的操作顺序 __bases__: 基类列表,可直接修改来更换基类,影响 mro 顺序。

In [100]: class A(object): pass
In [101]: class B(A): pass

In [102]: B.__mro__
# 先查找B,然后查找A
Out[102]: (__main__.B, __main__.A, object)

# 记录了B的父类
In [111]: B.__bases__
Out[111]: (__main__.A,)

抽象类

象类 (Abstract Class) 法实例化,且派 类必须 "完整" 实现所有抽象成员才可创建实例。

>> from abc import ABCMeta, abstractmethod, abstractproperty

>>> class UserAbstract(object):
        # 使用元类来控制,创建这个类需要检查是否实现print_id方法
...     __metaclass__ = ABCMeta

...     def __init__(self, uid):
...         self._uid = uid
...
...     @abstractmethod
...     def print_id(self): pass

操作符重载

称索引器,像序列或字典类型那样操作对象。

 def Items(object):

    def __init__(self):
        self.fields = {}

    def __getitem__(self, key):
        return self.fields.get(key)

    def __setitem__(self, key, value):
        self.fields[key] = value

    def __delitem__(self, key):
        del self.fields[key]

    def __contains__(self, key):
        return key in self.fields

getattr VS getattribute

先看看这几个法的触发时机

getattr: 访问不存在的成员。 • setattr: 对任何成员的赋值操作。 • delattr: 删除成员操作。 • getattribute: 访问任何存在或不存在的成员,包括 dict

不要在这几个方法里面直接访问对象成员 例如 self.key

也不要hasattr/getattr/setattr/delattr函数,例如getattr(self, key)

因为它们会被再次拦截,形成无限循环。

正确的做法是直接操作 self.__dict__['key']

__getattribute____dict__ 都会拦截,只能使用基类的 __getattribute__ 返回结果

直接使用[类名.字段]访问并不会进入__getattribute__拦截函数

 class ModelProxy(object)

    def __init__(self, t_obj):
        self.t_obj = t_obj

    def __getattribute__(self, name):

        # 使用ModelProxy.key并不会执行__getattribute__方法!!!
        # 所以可以使用getattr(type(self), name, None)
        # type(self)返回的是ModelProxy

        # 如果使用 getattr(self, name, None)则会进入无限循环
        value = getattr(type(self), name, None)

        # 如果不存在则抛弃异常, 抛出异常后会自动调用调用__getattr__
        if value is None:
            raise AttributeError
        elif callable(value):
            return lambda: value(self)
        else:
            return value

    def __getattr__(self, name):

        # 使用基类的__getattribute__,为什么基类的方法可以被调用
        # 是因为基类的__getattribute__并没有被重写
        # 还有一种调用基类的方法 super(ModelProxy, self).__getattribute__(self, "t_obj")
        t_obj = object.__getattribute__(self, "t_obj")
        return getattr(t_obj, name)

这里需要明白

1. 实例对象访问属性调用的是类定义的__getattribute__

2. 类型对象(类)访问静态属性调用时元类(默认是type对象)的__getattribute__

还有常用的重载方法有__call__

描述符

python定义了__get__, __set__的方法的类就是描述符。

TODO 描述符和属性的相关内容

results matching ""

    No results matching ""