阿小信大人的头像
做你说过的,说你能做的 阿小信大人

Python面向对象编程笔记2014-09-02 05:05

1 继承

如果有类A、B、C,派生关系为A->B->C,A中有f方法,B中没有重写,C中可以用B.f()调用。例如:

以下问题必须是在显示继承object这种新类的环境下才有的特性

super(cls, instance)返回一个特殊对象,该对象支持在基类上执行属性查找,使用该函数,python将使用本来应该在基类上使用的正常搜索规则来搜索属性,无需具体写出准确的方法位置并且能清晰地陈述意图。

mro属性,可打印他查看基类的顺序。

继承时,始终先检查当前派生出的类,然后再检查基类。如果多继承则按类定义中列出的父类顺序检查这些父类。

class X(object):pass
class Y(X):pass
class Z(X, Y):pass #错误,无法创建类,无法确定有意义的基类顺序

因为Z中,X出现在Y前面,所以会先检查类X,但是Y更加特殊,因为Y继承至X,因此如果先检查X,就不可能解析Y中更为特殊的方法,所以更特殊的应该写前面。

2 静态方法和类方法

在编写类时需要采用很多不同的方式来创建新实例常常会用到静态方法,因为类中只能有一个__init__函数,所以替代的创建函数通常如下定义:

class Date(object):
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    @staticmethod
    def now():
        t = time.localtime()
        return Date(t.tm_year, t.tm_mon, t.tm_day)
    @staticmethod
    def tommrow():
        t = time.localtime(time.time()+86400)
        return Date(t.tm_year, t.tm_mon, t.tm_day)
a = Date(2011,7,1)
b = Date.now()
c = Date.tomorrow()

类方法是将类本身作为对象进行操作。

class Times(object):
    factor = 1
    @classmethod
    def mul(cls, x):
        return cls.factor*x
class TwoTimes(Times):
    factor = 2
x = TwoTimes.mul(4) #调用Times.mul(TwoTimes, 4) -> 8

如果有类继承上面的Date类,那么派生类调用now的话返回的是Date对象,而不是派生类的对象,如果用@classmethod修饰now的话,那么返回的将是派生类的对象。

3 特性

方法本身是作为一类特性被隐式处理的。

使用@property修饰方法的话,调用时,不用加()就能执行方法中的代码。

特性可作拦截操作,以设置和删除属性。如下:

class F(object):
    def __init__(self, name):
        self.__name = name
    @property
    def name(self):
        return self.__name
    @name.setter
    def name(self value):
        self.__name = value
    @name.deleter
    def name(self, value):
        raise TypeError("Can;t delete")
f = F('Ashin')
n = f.name    #调用f.name() - get()
f.name = 'chen'       #调用setter name(f, 'chen')
del name     #调用deleter name(f)

方法的名称必须和原始属性的名称完全匹配,所存储属性的名称没有要求,但必须与特性名称不同,以便区分。

4 数据封装和私有属性

_xxx 不能用“from module import *”导入

__xxx__ 系统定义名字

__xxx 类中的私有变量名

类中以双下划线开头的名称都会自动变形,形成具有_类名__Foo形式的新名称

名称变形不会发生在getattr(), hasattr(), setattr(), 或delattr()等函数中发生,因为这些函数中,属性名被指定为字符串,对于这些函数需要显式的使用变形名称(如__Classname__name)来访问属性。

定义易变属性时,建议通过特性来使用私有属性

混淆私有类属性的命名和模块中的私有定义的命名

常见错误:在定义类时,在属性名上使用单个前下划线来隐藏属性值,这是错误的。

"单下划线" 开始的成员变量叫做保护变量,代表不能直接访问的类属性,只有类对象和子类对象自己能访问到这些变量;

"双下划线" 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。

在模块中,这种命名可以阻止通过from module import *语句导入名称,但是在类中,这种命名约定既不能隐藏属性,也不能阻止命名冲突,通过定义列表__all__,可以精确控制from module import *导入的名称集合。

from module import * 这种语法只能用在模块最顶层,不能用在函数体中。

5 对象内存管理

引用计算为0时,会调用__del__()方法,但是del语句通常不会直接调用__del__(),而是调用obj.__getattrribute__(),如果__getattrribute__()搜索失败则会调用__getattr__(),如果仍然失败这会报异常。

定义了__del__()的实例无法被Python的循环垃圾收集器收集,类会创建一个引用循环,引用计数永远不会达到0,也永远不会执行清楚操作,一种解决办法是使用weakref模块为一个类创建对其他类的弱引用。

弱引用是一种在不增加对象引用计数的情况下创建对象引用的方式,要创建弱引用必须添加额外的函数位来检查被引用的对象是否仍然存在。

class Observer(object):
    def __init__(self, ins):
        self.ins = ins
        ins.foo(self)
    def __del__(self):
        self.ins.unfoo(self)
        del self.ins
import weakref
class Observer(object):
    def __init__(self, ins):
        self.insref = weakref.ref(ins) #创建weakref
        ins.foo(self)
    def __del__(self):
        i = self.insref()  #获取数据
        if i:                    #如果仍然存在则注销
            i.unfoo(self)

6 __slots__

定义__slots__,可以限制对实例属性名称的设置。

class A(object):
    __slots__ = ('name', 'age')

这样可以阻止其他人向现有实例添加新属性。

使用他实际上是对内存和执行速度的一种性能优化,使用__slots__的的实例不再使用字典来存储实例数据,而是使用基于数组的数据结构。

父类如果定义了__slots__那么子类也要用__slots__

没有必要向__slots__添加方法或特性名称。

7 抽象类

>>> from abc import ABCMeta, abstractmethod, abstractproperty
>>> class A:
...   __metaclass__ = ABCMeta    #python3x中为class A(metaclass=ABCMeta)
...   @abstractmethod
...   def spam(self, a, b):
...     pass
...   @abstractproperty
...   def name(self):
...     pass
...
>>> a = A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class A with abstract methods name, spam
#有装饰器的方法必须实现
###################################################################
>>> class B(A):
...   def test(self):
...     pass
...
>>> b = B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class B with abstract methods name, spam
##################################################################
>>> class C(A):
...   def spam(self): #参数可以不相同
...     print 'hello'
...
>>> c = C()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class C with abstract methods name
###################################################################
>>> class D(A):
...   def spam(self):
...     print 'spam'
...   def name(self):
...     return 'name'
...
>>> d = D()
>>> d.spam()
spam
##################################################################
>>> class E():
...   def e(self):
...     print 'e'
...
>>> A.register(E)
>>> issubclass(A,E)
False
>>> issubclass(E,A)
True
>>> e = E()
>>> e.e()
e
>>> e.spam()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: E instance has no attribute 'spam'
>>> A.e()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'A' has no attribute 'e'

继承某个特定的抽象基类,来表示它们支持抽象基类接口

当你已经写了一个新的抽象基类,能描述一个存在的类型或类,或者你想声明某些第三方类实现了一个抽象基类,register()方法是有用的。

8 元类

定义类时,类定义本身就是一个对象

>>> class Foo(object):
...   pass
...
>>> isinstance(Foo, object)
True

元类就是知道如何创建和管理类的对象

控制Foo创建的元类是一个名为type的类

>>> type(Foo)
<type 'type'>

创建对象Foo:Foo = type(class_name, class_parents, class_dict),这个方法可以自定义,类可以显示自定其元类,通过设置__metaclass__类变量或在基类元组中提供metaclass关键字参数来实现

class Foo:
    __metaclass__ = type

没有显式指定元类,class语句将会检查基类元组中的第一个条目,这时,元类与第一个基类的类型相同

没有指定基类,class语句将会检查全局变量__metaclass__,找到该变量将用他来创建类,没有找到将使用默认元类

自定义元类

>>> class DocMeta(type):
...   def __init__(self, name, bases, dict):
...     for key, value in dict.items():
...       #跳过特殊方法和私有方法
...       if key.startswith('__'):continue
...       #跳过不可调用的任何方法
...       if not hasattr(value, '__call__'):continue
...       #检查doc字符串
...       if not getattr(value, '__doc__'):raise TypeError("%s must have a docstring"%key)
...     type.__init__(self, name,bases,dict)
...
>>> class Documented:
...   __metaclass__ = DocMeta
...
>>> class Foo(Documented):
...   def spam(self, a, b):
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in __init__
TypeError: spam must have a docstring
>>> class Foo(Documented):
...   def spam(self, a, b):
...     '''doc'''
...     pass
...
>>> 
#####################################
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'spam']
>>> dir(Foo.spam)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'im_class', 'im_func', 'im_self']

9 类装饰器

他接受类作为输入并返回类作为输出。例如:

>>> registry = {}
>>> def register(cls):
...   registry[cls.__clsid__] = cls
...   return cls
...
>>> @register
... class Foo(object):
...   __clsid__ = '2010081001'
...   def bar(self):
...     pass
...
>>> registry
{'2010081001': <class '__main__.Foo'>}
#也可以
>>> class Foo(object):
...   __clsid__ = '2010081001'
...   def bar(self):
...     pass
...
>>> registry = {}
>>> register(Foo)
<class '__main__.Foo'>
>>> registry
{'2010081001': <class '__main__.Foo'>}
>>>

如果您觉得从我的分享中得到了帮助,并且希望我的博客持续发展下去,请点击支付宝捐赠,谢谢!

若非特别声明,文章均为阿小信的个人笔记,转载请注明出处。文章如有侵权内容,请联系我,我会及时删除。

#Python#  
分享到:
阅读[1093] 评论[0]

你可能也感兴趣的文章推荐

本文最近访客

发表评论