面向对象的程序设计的核心是对象(上帝式思惟),要理解对象为什么物,必须把本身当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也能够创造出来。面向对象的程序设计比如如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题须要四我的:唐僧,沙和尚,猪八戒,孙悟空,每一个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性),然而这并很差玩,因而如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。而后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。python
面向对象的程序设计git
面向对象的程序设计并非所有。对于一个软件质量来讲,面向对象的程序设计只是用来解决扩展性。编程
因为Python是动态语言,根据类建立的实例能够任意绑定属性。
给实例绑定属性的方法是经过实例变量,或者经过self变量。安全
类名加括号就是实例化,会自动触发__init__函数的运行,能够用它来为每一个实例定制本身的特征。数据结构
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。ide
实例属性属于各个实例全部,互不干扰;类属性虽然归类全部,但类的全部实例均可以访问到。函数
在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,由于相同名称的实例属性将屏蔽掉类属性,可是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。测试
>>> class Student(object): ... name = 'Student' ... >>> s = Student() # 建立实例s >>> print(s.name) # 打印name属性,由于实例并无name属性,因此会继续查找class的name属性 Student >>> print(Student.name) # 打印类的name属性 Student >>> s.name = 'Michael' # 给实例绑定name属性 >>> print(s.name) # 因为实例属性优先级比类属性高,所以,它会屏蔽掉类的name属性 Michael >>> print(Student.name) # 可是类属性并未消失,用Student.name仍然能够访问 Student >>> del s.name # 若是删除实例的name属性 >>> print(s.name) # 再次调用s.name,因为实例的name属性没有找到,类的name属性就显示出来了 Student
可使用如下函数的方式来访问属性:spa
咱们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值设计
Python 使用了引用计数这一简单技术来跟踪和回收垃圾。
在 Python 内部记录着全部使用中的对象各有多少引用。
一个内部跟踪变量,称为一个引用计数器。
当对象被建立时, 就建立了一个引用计数, 当这个对象再也不须要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。可是回收不是"当即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。
垃圾回收机制不只针对引用计数为0的对象,一样也能够处理循环引用的状况。循环引用指的是,两个对象相互引用,可是没有其余变量引用他们。这种状况下,仅使用引用计数是不够的。Python 的垃圾收集器其实是一个引用计数器和一个循环垃圾收集器。做为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(及未经过引用计数销毁的那些)的对象。 在这种状况下, 解释器会暂停下来, 试图清理全部未引用的循环。
析构函数 __del__ ,__del__在对象销毁的时候被调用,当对象再也不被使用时,__del__方法运行
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是经过继承机制。
经过继承建立的新类称为子类(Subclass)或派生类,被继承的类称为基类、父类或超类(Base class、Super class)。
继承语法
class 派生类名(基类名) ...
在python中继承中的一些特色:
若是在继承元组中列了一个以上的类,那么它就被称做"多重继承" 。
语法:
派生类的声明,与他们的父类相似,继承的基类列表跟在类名以后,以下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]): ...
继承的定义 继承是一种建立新类的方式,在python中,新建的类能够继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。 分类:单继承和多继承 class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass print(SubClass2.__base__) print(SubClass2.__bases__) print(ParentClass1.__base__) 输出 <class '__main__.ParentClass1'> (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>) <class 'object'> 若是没有指定基类,python的类会默认继承object类,object是全部python类的基类,它提供了一些常见方法(如__str__)的实现。
继承有什么好处?最大的好处是子类得到了父类的所有功能。继承的另外一个好处:多态。
要判断class的类型,可使用isinstance()函数。isinstance()判断的是一个对象是不是该类型自己,或者位于该类型的父继承链上。在继承关系中,若是一个实例的数据类型是某个子类,那它的数据类型也能够被看作是父类。
多态真正的威力:调用方只管调用,无论细节,这就是著名的“开闭”原则,对扩展开放:容许新增子类;对修改封闭:不须要修改依赖类的函数。
继承还能够一级一级地继承下来,就比如从爷爷到爸爸、再到儿子这样的关系。而任何类,最终均可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。
Python是动态语言,动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就能够被看作是鸭子。动态语言的鸭子类型特色决定了继承不像静态语言那样是必须的。
若是你的父类方法的功能不能知足你的需求,你能够在子类重写你父类的方法:
下表列出了一些通用的功能,你能够在本身的类重写:
序号 | 方法, 描述 & 简单的调用 |
---|---|
1 | __init__ ( self [,args...] ) 构造函数 简单的调用方法: obj = className(args) |
2 | __del__( self ) 析构方法, 删除一个对象 简单的调用方法 : del obj |
3 | __repr__( self ) 转化为供解释器读取的形式 简单的调用方法 : repr(obj) |
4 | __str__( self ) 用于将值转化为适于人阅读的形式 简单的调用方法 : str(obj) |
5 | __cmp__ ( self, x ) 对象比较 简单的调用方法 : cmp(obj, x) |
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
在类的内部,使用 def 关键字能够为类定义一个方法,与通常函数定义不一样,类方法必须包含参数 self,且为第一个参数
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
Python不容许实例化的类访问私有数据,但你可使用 object._className__attrName( 对象名._类名__私有属性名 )访问属性
__foo__: 定义的是特殊方法,通常是系统定义名字 ,相似 __init__() 之类的。特殊变量能够直接访问
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能容许其自己与子类进行访问,不能用于 from module import *,意思就是,“虽然我能够被访问,可是,请把我视为私有变量,不要随意访问”。
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是容许这个类自己进行访问了。
使用__slots__
若是咱们想要限制实例的属性怎么办?好比,只容许对Student实例添加name和age属性。
为了达到限制的目的,Python容许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Student(object): __slots__ = ('name', 'age') # 用tuple定义容许绑定的属性名称 >>> s = Student() # 建立新的实例 >>> s.name = 'Michael' # 绑定属性'name' >>> s.age = 25 # 绑定属性'age' >>> s.score = 99 # 绑定属性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score' 因为'score'没有被放到__slots__中,因此不能绑定score属性,试图绑定score将获得AttributeError的错误。 使用__slots__要注意,__slots__定义的属性仅对当前类实例起做用,对继承的子类是不起做用的: >>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999
实例方法,除静态方法与类方法外,类的其余方法都属于实例方法。
实例方法须要将类实例化后调用,若是使用类直接调用实例方法,须要显式地将实例做为参数传入。
类调用:类名.方法(实例对象名)
对象调用:直接调用(实例对象名.方法)
class Person:
def run(self):
pass
class ClassA(object): def func_a(self): print('Hello Python') # 使用实例调用实例方法 ca = ClassA() ca.func_a() # 若是使用类直接调用实例方法,须要显式地将实例做为参数传入 ClassA.func_a(ca)
类方法传入的第一个参数为cls,是类自己。而且,类方法能够经过类直接调用,或经过实例直接调用。但不管哪一种调用方式,最左侧传入的参数必定是类自己。
类方法使用@classmethod装饰器来声明。
class Person:
@classmethod
def countPerson(cls):
pass
类调用:不用手动传递第一个参数,会自动的把调用的类自己给传递过去
对象调用:不用手动传递第一个参数,会自动的把调用的对象对应的类给传递过去
class Person: @classmethod def leifangfa(cls, a): print("这是一个类方法", cls, a) Person.leifangfa(123) p = Person() p.leifangfa(666) # 输出: # 这是一个类方法 <class '__main__.Person'> 123 # 这是一个类方法 <class '__main__.Person'> 666
静态方法是指类中无需实例参与便可调用的方法(不须要self参数),静态方法使用@staticmethod装饰器来声明。
类调用:直接调用(类名.方法)
对象调用:直接调用(实例对象名.方法)
class Person:
@staticemethod
def countPerson():
pass
class ClassA(object): @staticmethod def func_a(): print('Hello Python') if __name__ == '__main__': ClassA.func_a() #类调用 ca = ClassA() ca.func_a() #实例对象调用
class ClassA(object): def func_a(): print('Hello Python') if __name__ == '__main__': ClassA.func_a() ca = ClassA() ca.func_a() 异常信息: func_a() takes 0 positional arguments but 1 was given 由于func_a没有声明为静态方法,类实例在调用func_a时,会隐式地将self参数传入func_a,而func_a自己不接受任何参数,从而引起异常。
1.定义形式上:
a. 类方法和静态方法都是经过装饰器实现的,实例方法不是;
b. 实例方法须要传入self参数,类方法须要传入cls参数,而静态方法不须要传self或者cls参数。
2. 调用方式上:
实例方法只能经过实例对象调用;类方法和静态方法能够经过类对象或者实例对象调用,若是是使用实例对象调用的类方法或静态方法,最终都会转而经过类对象调用。
3. 应用场景:
a. 实例方法使用最多,能够直接处理实例对象的逻辑;类方法不须要建立实例对象,直接处理类对象的逻辑;静态方法将与类对象相关的某些逻辑抽离出来,不只能够用于测试,还能便于代码后期维护。
b. 实例方法和类方法,可以改变实例对象或类对象的状态,而静态方法不能。
实例对象由类建立出来,类是一个对象,类由元类建立出来。
metaclass容许建立类或者修改类。能够把类当作是metaclass建立出来的“实例”。
Python解释器遇到class定义时,先扫描class定义的语法,而后调用type()函数建立出class。
要建立一个class对象,type()函数依次传入3个参数:
class的名称;
继承的父类集合,注意Python支持多重继承,若是只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定。
>>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 建立Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '__main__.Hello'>
S(Single Responsibility Principle)单一职责原则,一个类只负责一项职责
好处:易于维护, 写出高内聚的代码;易于代码复用
O(Open Closed Principle) 开放封闭原则
对扩展开放
对修改关闭
易于维护, 保证代码安全性以及扩展性
L(Liskov Substitution Principle) 里氏替换原则,使用基类引用的地方必须能使用继承类的对象
好处
防止代码出现不可预知的错误
方便针对于基类的测试代码, 能够复用在子类上
I(Interface Segregation Principle)接口分离原则
若是一个类包含了过多的接口方法,而这些方法在使用的过程当中并不是"不可分割", 那么应当把他们进行分离
所谓接口, 在Python中, 能够简单的理解为"抽象方法"
好处
提升接口的重用价值
D(Dependency Inversion Principle)依赖倒置原则 高层模块不该该直接依赖低层模块 他们应该依赖抽象类或者接口 好处 利于代码维护