面向对象编程
1、和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。Python解释器自己会把实例变量传进去。
2、和静态语言不同,Python允许对实例变量绑定任何数据。
3、访问限制。私有变量(变量名称以__开头)不可以被外部访问
4、变量名类似__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的。因此,变量名称一定不要设成__xxx__
5、继承可以把父类的所有功能都直接拿过来。
6、多态的意义:当我们需要传入Dog
、Cat
、Tortoise
……时,我们只需要接收Animal
类型就可以了,因为Dog
、Cat
、Tortoise
……都是Animal
类型,然后,按照Animal
类型进行操作即可。由于Animal
类型有run()
方法,因此,传入的任意类型,只要是Animal
类或者子类,就会自动调用实际类型的run()
方法。(API的接口类型不固定)
7、动态语言的鸭子特性:animal只是个形参,只要传入的参数是一个有run()方法的对象就可以。对于静态语言(例如Java)来说,传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
class Animal(object):
pass
def run_twice(Animal):
Animal.run()
8、对象或者变量的类型
isinstance(a,b)
判断的是一个对象a是否是某种类型。继承的子类的对象属于父类
type():可以是int,str,types.FunctionType,types.BuiltinFunctionType等等
9、实例属性和类属性
类属性的绑定在类内完成:
class student(object):
self.name=....
实例属性的绑定:
s=student()
s.name=...
当实例属性和类属性的名称相同且同时存在时,实例属性的优先级会高于类属性。
面向对象高级编程
- 枚举类
from enum import Enum, unique
@unique
class Gender(Enum):
Male = 0
Female = 1
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
bart = Student('Bart', Gender.Male)#gender就由字符串类型变成了int类型
- 利用__function__定制类
python的class中有很多有特殊用途的函数,它们的格式都是这样的:__function__(注意前后都是双下划线)
1.__str__()
class student(object):
...
print(student('jane'))
此时在打印时,调用了__str__函数
2.__repr__()
s=student('jane')
s
__repr__是在直接敲变量而不是print时调用的
一个简单做法:__repr__=__str__
3.__iter__()和__next__()用于for...in...迭代语句
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误(异常情况)时退出循环。
4.__getitem__()
__iter__和__next__无法返回一个list类型的结果。
5.__getattr__()
当调用不存在的属性时,比如score
,Python解释器会试图调用__getattr__(self, 'score')
来尝试获得属性
作用:动态化处理类的属性和方法的调用。e.g.链式调用调用API
6.__call__()
s = Student('Michael')
当定义__call__()后,s除了是一个实例外,还可以当作一个函数
可以通过callable()
函数判断某一个对象是不是可调用的
7.__len__()
__slots__
创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
但是,在实际中,我们往往不希望某个类的实例可以被绑定任意的属性和方法。因此可以给__slots__变量用赋值一个tuple,来定义能被添加的属性名称。要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
- @property(装饰器)
如果直接用类的属性初始化,就无法检查属性的参数是否正确。因此,还是需要set和get方法来对实例进行操作。这样操作起来会比较复杂。@property是python内置的一个装饰器函数,它可以把class的方法变成属性。
把一个getter方法变成属性,只需要加上@property
就可以了,此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值。如果该属性是一个只读属性,那就不需要@score.setter了。
@property
def width(self):
return self._width
@width.setter
def width(self,value):
self._width=value
一定要注意的一点是:属性和方法一定不可以重名,解决方法,在属性前面加_,否则方法会一直调用它本身。
- 多重继承
在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich
继承自Bird
。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich
除了继承自Bird
外,再同时继承Runnable
。这种设计通常称之为MixIn。注意:JAVA就是只允许单一继承。
def dog(animal,runnable_mixin):
pass
使用元类
用type()函数动态地创建类
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
如果要控制类的创建行为,还可以使用metaclass。(这个目前超出了我的理解范围,以后再来填坑)