python面向对象编程OOP

在python中,所有数据类型都可以视为对象,当然也可以自定义对象。

面向对象技术简介

类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
实例变量:定义在方法中的变量,只作用于当前实例的类。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。
实例化:创建一个类的实例,类的具体对象。
方法:类中定义的函数。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

私有化和访问限制

Python 使用一种约定的方式来,声明访问限制的粒度。
xx :这种方式命名的变量或者方法,都是 public 粒度的;
_xx :这种方式命名的变量或者方法,都是 protected 粒度的;
__xx :私有属性的变量或者方法,外部调用会报错的。

特殊类属性

C.__name__ 类C的名字(字符串)
C.__doc__ 类C的文档字符串
C.__bases__ 类C的所有父类构成的元组
C.__dict__ 类C的属性
C.__module__ 类C定义的所载的模块
C.__class__ 实例C对应的类

特殊类方法

C.__init__(self[, arg1, …]) 构造器
C.__new__(self[, arg1, …]) 构造器,通常用在设置不变数据类 型的子类。
C.__del__(self) 解构器
C.__str__(self) 可打印的字符输出;内建 str()及 print 语句
C.__repr__(self) 运行时的字符串输出;内建 repr() 和‘ 操作符
C.__unicode__(self) Unicode 字符串输出;内建 unicode()
C.__call__(self, *args) 表示可调用的实例
C.__cmp__(self, obj) 对象比较;内建 cmp()
属性
C.__getattr__(self, attr) 获取属性;内建 getattr();仅当属性没有找到时调用
C.__setattr__(self, attr, val) 设置属性
C.__delattr__(self, attr) 删除属性
C.__getattribute__(self, attr) 获取属性;内建 getattr();总是被调用
C.__get__(self, attr) ( 述符)获取属性
C.__set__(self, attr, val) ( 述符)设置属性
C.__delete__(self, attr) ( 述符)删除属性

可以根据需要,使用特殊方法定制类。

实例

实例化

类实例化需要两个步骤:
1.调用__new__()方法创建实例,__new__()方法自动从object继承
2.调用__init__()方法对其初始化,__init__()方法在类中定义
这两个方法的的调用在类实例化的过程中是在背后默默进行的,我们是看不到他是什么时候调用的以及如何调用的,你实例化后自动在后台进行完了。

__init__() “构造器”方法

当类被调用,实例化的第一步是创建实例对象。一旦对象创建了,Python 检查是否实现了 __init__()方法。默认情况下,如果类中没有定义(或覆盖)特殊方法__init__(),对实例不会施加任 何特别的操作.任何所需的特定操作,都需要程序员实现__init__(),覆盖它的默认行为。如果 __init__()没有实现,则返回它的对象,实例化过程完毕。

然而,如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递 进去,像标准方法调用一样。调用类时,传进的任何参数都交给了__init__()。实际中,你可以想 像成这样:把创建实例的调用当成是对构造器的调用。

__new__() “构造器”方法

与__init__()相比,__new__()方法更像一个真正的构造器。类型和类在版本 2.2 就统一了, Python 用户可以对内建类型进行派生,因此,需要一种途径来实例化不可变对象,比如,派生字符 串,数字,等等。

在这种情况下,解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例 化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)。

为何我们认为__new__()比__init__()更像构造器呢?这是因为__new__()必须返回一个合法 的实例,这样解释器在调用__init__()时,就可以把这个实例作为 self 传给它。调用父类的__new__()
来创建对象,正像其它语言中使用 new 关键字一样。

__del__() “解构器”方法

同样,有一个相应的特殊解构器(destructor)方法名为__del__()。然而,由于 Python 具有 垃圾对象回收机制(靠引用计数),这个函数要直到该实例对象所有的引用都被清除掉后才会执行。 Python 中的解构器是在实例释放前 供特殊处理功能的方法,它们通常没有被实现,因为实例很少 被显式释放。

解构器是在类 C 实例所有的引用都被清除掉后,才被调用的,比如, 当引用计数已减少到 0。如果你预期你的__del__()方法会被调用,却实际上没有被调用,这意味着, 你的实例对象由于某些原因,其引用计数不为 0,这可能有别的对它的引用,而你并不知道这些让 你的对象还活着的引用所在。

另外,要注意,解构器只能被调用一次,一旦引用计数为 0,则对象就被清除了。这非常合理, 因为系统中任何对象都只被分配及解构一次。

小结:
不要忘记首先调用父类的__del__()。
调用 del x 不表示调用了 x.__del__() —–前面也看到,它仅仅是减少 x 的引用计数。
如果你有一个循环引用或其它的原因,让一个实例的引用逗留不去,该对象的__del__()可
能永远不会被执行。
__del__()未捕获的异常会被忽略掉(因为一些在__del__()用到的变量或许已经被删除了)。
不要在__del__()中干与实例没任何关系的事情。
除非你知道你正在干什么,否则不要去实现__del__()。
如果你定义了__del__,并且实例是某个循环的一部分,垃圾回收器将不会终止这个循环—
—你需要自已显式调用 del。

self

  • self在定义时需要定义,但是在调用时会自动传入。
  • self的名字并不是规定死的,但是最好还是按照约定是用self
  • self总是指调用时的类的实例。

实例及其他对象的内建函数

  • issubclass(sub, sup) 如果类 sub 是类 sup 的子类,则返回 True,反之,为 False。
  • isinstances(obj1, obj2) 如果实例obj1是类obj2或者obj2子类的一个实例;或者如果obj1 是 obj2 的类型,则返回 True;反之,为 False。
  • hasattr(obj, attr)如果 obj 有属性 attr(用字符串给出),返回 True,反之为False.
  • getattr(obj, attr[, default]) 获取 obj 的 attr 属性;与返回 obj.attr 类似;如果 attr不是 obj 的属性,如果 供了默认值,则返回默认值;不然, 就会引发一个 AttributeError 异常。
  • setattr(obj, attr, val)设置obj的attr属性值为val,替换任何已存在的属性值; 不然,就创建属性;类似于 obj.attr=val
  • delattr(obj, attr)从 obj 中删除属性 attr(以字符串给出);类似于 del obj.attr。
  • dir(obj=None)返回 obj 的属性的一个列表;如果没有给定 obj,dir()则显示局部名字空间空间中的属性,也就是 locals().keys()
  • super(tyep, obj=None)返回一个表示父类类型的代理对象;如果没有传入 obj, 则返 回的 super 对象是非绑定的;反之,如果 obj 是一个 type , issubclass(obj,type) 必 为 True ; 否则isinstance(obj,type)就必为 True。
  • vars(obj=None)返回 obj 的属性及其值的一个字典;如果没有给出 obj, vars()显示局部名字空间字典(属性及其值),也就是 locals()。

 

 

面向对象进阶

类属性,实例属性,私有属性

尽量把需要用户传入的属性作为实例属性,而把同类都一样的属性作为类属性。实例属性在每创造一个实例时都会初始化一遍,不同的实例的实例属性可能不同,不同实例的类属性都相同。从而减少内存消耗。
1:实例属性:
最好在__init__(self,…)中初始化
内部调用时都需要加上self.
外部调用时用instancename.propertyname
2:类属性:
在__init__()外初始化
在内部用classname.类属性名调用
外部既可以用classname.类属性名又可以用instancename.类属性名来调用
3:私有属性:
1):单下划线_开头:只是告诉别人这是私有属性,外部依然可以访问更改
2):双下划线__开头:外部不可通过instancename.propertyname来访问或者更改
实际将其转化为了_classname__propertyname

静态方法,类方法,实例方法

1:实例方法:
def fun_name(self,…):
pass
只能外部用实例调用
2:静态方法:@staticmethod
不能访问实例属性!!! 参数不能传入self!!!
与类相关但是不依赖类与实例的方法!!
3:类方法:@classmethod
不能访问实例属性!!! 参数必须传入cls!!!
必须传入cls参数(即代表了此类对象–区别–self代表实例对象),并且用此来调用类属性:cls.类属性名
*静态方法与类方法都可以通过类或者实例来调用。其两个的特点都是不能够调用实例属性

 

super方法

super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。总之前人留下的经验就是:保持一致性。要不全部用类名调用父类,要不就全部用 super,不要一半一半。

更多参考:

https://laike9m.com/blog/li-jie-python-super,70/

http://www.jackyshen.com/2015/08/19/multi-inheritance-with-super-in-Python/

 

@property

todo

 

__slots__类属性

字典位于实例的“心脏”。__dict__属性跟踪所有实例属性。举例来说,你有一个实例 inst.它 有一个属性 foo,那使用 inst.foo 来访问它与使用 inst.__dict__[‘foo’]来访问是一致的。

字典会占据大量内存,如果你有一个属性数量很少的类,但有很多实例,那么正好是这种情况。 为内存上的考虑,用户现在可以使用__slots__属性来替代__dict__。

基本上,__slots__是一个类变量,由一序列型对象组成,由所有合法标识构成的实例属性的集 合来表示。它可以是一个列表,元组或可迭代对象。也可以是标识实例能拥有的唯一的属性的简单 字符串。任何试图创建一个其名不在__slots__中的名字的实例属性都将导致 AttributeError 异常。

这种特性的主要目的是节约内存。其副作用是某种类型的”安全”,它能防止用户随心所欲的动态 增加实例属性。带__slots__属性的类定义不会存在__dict__了(除非你在__slots__中增加 ‘__dict__’元素)。

元类(Metaclasses)和__metaclass__

元类被称为 Python 中的“深奥的巫术”。尽管你需要用到它的地方极少(除非你基于 zope 编程),可事实上它的基础理论其实令人惊讶地易懂。

一切皆对象,类也是对象,它们的类型是 type
一切都有类型, “class”和“type”之间本质上并无不同
以前,术语 type 用于内置类型,而术语 class 用于用户定义的类,但自 Pythoon 2.2 以来“class”和“type”本质上并无不同。

对于旧风格(old-style)类的类型是 types.ClassType。

类的类就 是它的元类。
就像对象是类的实例一样,类是它的元类的实例。
调用元类可以创建类。
确切来说,Python 中的其它对象也是如此。
因此当你创建一个类时,解释器会调用元类来生成它。

元类将用在创建使用了它的新类时调用,这里是一些关于这样做的好处的观点:
装饰(Decorate)类的所有方法,用以日志记录或者性能剖分。
自动 Mix-in 新方法
在创建时注册类。(例如自动注册插件或从类成员创建数据库模式。)
提供接口注册,功能自动发现和接口适配。
类校验:防止子类化,校验所有的方法是否都有 docstrings。

References

《python核心编程》
《python 学习笔记》
http://www.jianshu.com/p/212b6fdb2c50
http://blog.csdn.net/gzlaiyonghao/article/details/3048947
http://www.cnblogs.com/pengsixiong/p/4823473.html
http://www.runoob.com/python/python-object.html
http://www.liaoxuefeng.com/

暂无评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注