颇有一段时间没使用python了,前两天研究微信公众号使用了下python的django服务,感受好多知识都遗忘了,毕竟以前没有深刻的实践,长期不使用就忘得快。本博的主要目的就是对Python中我认为重要的面对对象知识进行总结,一是加深记忆,而是方便往后遗忘了好迅速的捡起来。html
目录索引:python
(1)隐式的基类object程序员
(2)python中类和对象的区别算法
(3)实现python风格类所用特殊方法django
(a)__all__和__slots__编程
(b)__format__()、__repr__()和__str__()设计模式
(c)__init__()方法微信
(d)__new__()和__del__()函数
(e)__call__()方法post
(f)属性访问、特性和修饰符
(g)@classmethod和@staticmethod
(h)关于私有属性的说法
(4)抽象基类和继承
每一个Python类的定义都回隐式继承自object类,它的定义很是简单,几乎什么行为都不包括。
能够看到类定义就是对type类的一个对象的类型声明,基类为object,相应的派生自object类中的对象方法也将继承各自相应的默认实现,在某些状况下,基类中一些特殊方法的默认行为也正是咱们想要的,对于一些特殊状况,就须要重写这些方法。
Python2 没有默认继承object
Python3 默认所有继承object类,都是新式类
(1)类属性和对象属性的区别
对象能够经过 对象名.属性名 调用对象属性和类属性
类能够经过 类名.属性名 调用类的属性,可是不能调用对象的属性
class People(object): # 类属性是指定义在类的内部并且在方法的外部的属性 money = 10000 def __init__(self,name,age,gender=1): # 对象属性是指定义在方法的内部的属性,例如本例中 # name,age和gender都是对象属性 self.name = name self.age = age self.gender = gender student1 = People("张三",20) student2 = People("李四",25) print(student2.name) print(student2.money) print(People.money) print(People.name)
再注意看下面代码输出结果:
print(id(student1.money))
print(id(student2.money))
print(id(People.money))
说明:对象student一、student2和类People的属性money的内存地址都是相同的
继续往下看
student1.money -= 1000
print("student1.money:", student1.money)
print("student1.money id:",id(student1.money))
print("student2.money:", student2.money)
print("student2.money id:",id(student2.money))
print("People.money id:",id(People.money))
说明:student1引用的money属性的内存地址已经和另外两个的不同了而另外两个的内存地址却仍是同样的
缘由:在通过表达式student1.money -= 1000 的过程时,会先在对象中查找是否有money这个属性,若是有的话,则直接进行运算若是没有,则会去类中查找是否有money属性,若是在类中找到money属性,那么student1就会建立一个对象属性money,在第二次调用的时候就会调用本身的对象属性,而不是类People中的属性了,而student2由于没有通过运算,因此不会建立本身的money属性,而是引用类People的属性,因此student2和People引用的仍是同一个属性
关于__all__ = []总结两点:
A:在__init__.py文件中
表示形式: __all__=["module_a","module_b"] 在使用 from package_name import * 时 , 表示import 该package 中的 两个module及 两个module相关的类、方法等。
B:在普通的*.py中
表示形式: __all__=["class_name","function_name"] 在使用 from module_name import * 时,表示import 该module中的__all__中所列出的。
__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__定义的属性仅对当前类起做用,对继承的子类是不起做用的
>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999
除非在子类中也定义__slots__,这样子类容许定义的属性就是自身的__slots__加上父类的__slots__
上面的三个方法都是为了为Python对象提供一个很好的字符串表示
一般str()方法表示的对象对用户更加友好,这个方法由__str__()实现
repr()方法的表示一般会被更加技术化,这个方法由__repr__()实现
{!r} 调用__repr__方法
{!s} 调用__str__方法
string.format()和内置的format()函数都使用__format()__方法,都是为了得到给定对象的一个符合要求的字符串表示
直接上例子能更好的进行说明:
class People: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __str__(self): #return "{__class__.__name__}(name={0},age={1},sex={2})" .format(self.name,self.age,self.sex,__class__=self.__class__) return "__str__:(name={name},age={age},sex={sex})" .format(__class__=self.__class__,**self.__dict__) def __repr__(self): return "__repr__:{__class__.__name__}(name={name},age={age},sex={sex})" .format(__class__=self.__class__,**self.__dict__) def __format__(self, format_spec): if format_spec == "": return str(self) return format_spec.replace("%n",self.name).replace("%s",self.sex) if __name__ == "__main__": people = People("john",18,"man") print("{0!s}".format(people)) print("{0!r}".format(people)) print("format:",format(people,"%n-%s")) print("__format__:",people.__format__("%n-%s")) print("format:",format(people))
__init__(self)就如同类的构造函数,尽可能在构造函数中实现属性清单,这里就很少作介绍
下面主要总结下Python继承中super()方法的使用:
super实现原理:经过c3算法,生成mro(method resolution order)列表,根据列表中元素顺序查询调用新式类调用顺序为广度优先,旧式类为深度优先
super相似于嵌套的一种设计,当代码执行到super实例化后,先去找同级父类,若没有其他父类,再执行自身父类,再往下走,简单说就是子类在父类前,全部类不重复调用,从左到右(见下面例子的D.mro()打印)
Python2 super调用 super(开始类名,self).函数名()
Python3 super().函数名()
上例子更能说明上述问题:
class A(): def go(self): print ("go A go!") def stop(self): print ("stop A stop!") def pause(self): raise Exception("Not Implemented") class B(A): def go(self): super(B, self).go() print ("go B go!") class C(A): def go(self): super(C, self).go() print ("go C go!") def stop(self): super(C, self).stop() print ("stop C stop!") class D(B,C): def go(self): super(D, self).go() print ("go D go!") def stop(self): super(D, self).stop() print ("stop D stop!") def pause(self): print ("wait D wait!") class E(B,C): pass a = A() b = B() c = C() d = D() e = E() # 说明下列代码的输出结果 d.go() print('--------') e.go() print('--------') d.stop() print('--------') e.stop() print(D.mro()) a.pause() b.pause() c.pause() d.pause() e.pause()
__ new__ ()在__ init__()以前被调用,用于生成实例对象.利用这个方法和类属性的特性能够实现设计模式中的单例模式.单例模式是指建立惟一对象吗,单例模式设计的类只能实例化一个对象.
class Singleton(object): __instance = None # 定义实例 def __init__(self): pass def __new__(cls, *args, **kwd): # 在__init__以前调用 if Singleton.__instance is None: # 生成惟一实例 Singleton.__instance = object.__new__(cls, *args, **kwd) return Singleton.__instance
__new__()方法的使用和Python元编程相关,能够查看我以前的博客:Python元编程
__del__()方法涉及到Python对象销毁,Python文档中用不稳定性来描述__del__()方法的这种行为,而且提供了额外的关于异常处理的注释,总之,该函数的使用要慎重之慎重。
__ del__称做析构方法
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法通常无须定义,由于在Python中,程序员在使用时无需关心内存的分配和释放,由于此工做都是交给Python解释器来执行,因此,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。在程序执行结束以后,执行此方法
Python可调用对象:函数,方法,使用 yield 关键字的函数或方法,类的实例
__call__方法将类的实例变成可调用对象
>>>class Reader(): def __init__(self,name,nationality): self.name = name self.nationality = nationality def __call__(self): print('Reader: %s Nationality: %s' % (self.name, self.nationality)) >>>r = Reader('Annie','Chinese') >>>r() Reader:Annie Nationality: Chinese
以前的博客Python之属性、特性和修饰符做了详细说明
classmethod 修饰符对应的函数不须要实例化,不须要 self 参数,但第一个参数须要是表示自身类的 cls 参数,能够来调用类的属性,类的方法,实例化对象等。
#!/usr/bin/python # -*- coding: UTF-8 -*- class A(object): bar = 1 def func1(self): print ('foo') @classmethod def func2(cls): print ('func2') print (cls.bar) cls().func1() # 调用 foo 方法 A.func2() # 不须要实例化
staticmethod修饰符实现静态方法,该方法不强制要求传递参数
#!/usr/bin/python # -*- coding: UTF-8 -*- class C(object): @staticmethod def f(): print('runoob'); C.f(); # 静态方法无需实例化 cobj = C() cobj.f() # 也能够实例化后调用
Python并无真正的私有化支持,但可用下划线获得伪私有
我的习惯:
(1)_XXX 单下划表明protected
(2)__XXX 双下划线开始的且不以_结尾表示private(下面说明)
(3)__XXX__系统定义的属性和方法
看下面的例子:
class People: __name="zhanglin" def __init__(self): self.__age = 16 print(People.__dict__) p = People() print(p.__dict__)
会发现__name和__age属性名都发生了变化,都变成了(_类名+属性名),只有在__XXX这种命名方式下才会发生变化,因此以这种方式做为伪私有说明
该主题内容请查看以前的博客:Python之抽象基类