1、类(class)和实例(instances)是1对N的关系。
2、class由若干函数、变量(类成员)、属性(实例成员)组成。
一个示例的类如下:
class Account(object): num_account = 0 #类成员,所有示例共享! def __init__(self,name,balance): #构造函数 self.name = name #实例成员 self.balance = balance #实例成员 Account.num_account += 1 #更新类成员 def withdraw(self,amt): #注意一定要有self self.balance -= amt #更新实例成员
3、类的实例化
#实例化,会调用__init__函数 a = Account("He",1000) b = Account("Rui",2000) #操作 ( attribute binding ),会先检查有无该属性或者函数,注意无self参数,显然的。。 a.withdraw(100)
4、Python无类作用域,即对实例(无论是函数还是属性)的操作都必须加self完成。
class Foo(object): def bar(self): print("bar!") def spam(self): bar(self) #错误! self.bar() #正确!
5、python中的继承:基类( base class )、派生类 ( derived class ),派生类继承基类的所有属性和方法,并可以选择重写或移除。object是所有Python对象的基类(和Java的Object一样)。一个继承类的写法很简单,就是在def时class B(A):,A为基类,B为派生类。
class EvilAccount(Account): def badwithdraw(self,amt): self.balance -= 2 * amt
而调用是,依然是
c = EvilAccount("Me",1500) #继承了基类的__init__方法
c.badwithdraw(1000) #直接定位到派生类
c.withdraw(1000) #派生类没有,定位到基类
6、派生类调用基类构造函数:多数发生在派生类的构造函数与基类参数不同时。
class EvilAccount(Account): def __init__(name,balance,factor): #派生类与基类的构造函数不同 Account.__init__(name,balance) #派生类调用基类的构造函数 self.f = factor #然后初始化自己的特有成员变量
7、派生类如何调用父类的函数:
(1)self.xxx(abcd)
(2)super(cls, instance).xxx(abcd)
例如:
class MoreEvilAccount(EvilAccount): def deposit(self, amount): self.withdraw(5.00) super(MoreEvilAccount,self).desposit(amount) #调用父类的desposit方法
8、Python支持多继承,但属性间的冲突会导致很多问题,所以不建议使用。
9、多态/Duck Typing:Python一直都是运行时决定类型,因此有人称之为Duck Typing,即“鸭式类型”。好处是,可以定义一些内部方法、属性很类似但又无继承关系的类。例如标准库中的file-like文件。
10、静态方法和类方法。
可以说,类方法是静态方法的一个拓展吧(不再局限于类了)
class Foo(object): factor = 1 @staticmethod def add(x,y): return x+y @classmethod def mul(cls,s): return cls.factor*x
标准的调用方法都一样:
Foo.add(3,4)
Foo.mul(4,5)
除此之外,python并没有限定类方法和静态方法不能用于实例上,因此如下也是合法的:
f = Foo()
f.add(3,4)
f.mul(5,6)
11、属性标记@property,标记后,可以向访问实例属性一样自动get,如下:
#给函数加@property属性 class Cicle(object): def __init__(self,radius): self.radius = radius @property def area(self): return math.pi * self.radius *2 #调用时可视作属性一样get >>> c = Circle(4.0) >>> c.radius >>> 4.0
如果不加上述@property,则返回值c.radius会被视为是函数area的实例。
m = c.radius #m是函数实例
真正的m()时,才会调用c.radius()
12、一般来说,最好用getter和setter对实例内的属性进行保护,上面的@property只是getter方法,如何实现让函数类似的属性可以被赋值呢?需要@xxx.setter和@xxx.deleter。如下:
class Cicle(object): def __init__(self,area): self.__area__ = area @property def area(self): return self.__area__ @area.setter def area(self,value): self.__area__ = value @area.deleter def area(self): raise TypeError("can't delete name.")
标签@area.deleter的.前面的area必须完全匹配@property标记的属性!
这样后,就可以如下用啦!
c = Cicle()
a = c.area
c.area = 123.2
del c.area
13、也可以用用户自定义的get和set方法来做隔离。它们是:__get__()、__set__()、__delete__()函数。
14、关于私有函数/变量。根据Python约定,以下划线_开头的,可视为私有变量,但无明确语法限制。
15、对象的内存管理:创建对象时,会调用class的__new__()和__init__()。
__new__()的用法一般很罕见,主要用途为:
(1)从一些不可变类继承时,在new中更改一些数值,入string,tuple等。
class UpperStr(str): def __new__(cls,value=""): return str.__new__(cls,value.upper()) u = UpperStr("hello") #value is "HELLO"
(2)__new__()用于metaclass,后面会讲到
16、对象的管理也是基于引用计数:reference counting。当rc降到0的时候,会删除对象,此时调用自定义的__del__()如果有的话。__del__()一般也无需定义,除非你要显示的关闭文件、关闭网络socket、释放链接等操作。一般这些都不应该在__del__()中完成,和Java的finalize()道理一样。
17、引用计数rc并非完美,又是会产生‘“环引用”,导致内存泄漏,典型的就是“观察者模型”,此时可用弱引用解决问题。
import weakref class AccountObserver(object): def __init__(self, theaccount): self.accountref = weakref.ref(theaccount ) #create weakref
18、默认情况下,python的属性名集合是用dirtionary(类似map)的结构实现的,记录在cls.__dict__中。
当实例的任何属性self.xxx变化时,都会反应到cls.__dict__中。访问属性时,最终会调用内置的__getattr__()、__setattr__()和__delattr__(),如果需要,可以在这三个地方做拦截器(记录日志之类的)。
19、可以替换属性集合的内置数据结构,采用__slot__,它将限定属性可使用的名称,换来更小的内存和更快的运行时间。
import weakref class AccountObserver(object): __slot__ = ('name', 'balance') .... #属性只能命名为name和balance
由于不使用__dict__,对属性的访问也不会在进入__getattr__()等函数了。
此外,在继承的时候,子类必须也定义__slot__,否则内存消耗会更大!因此继承时候要谨慎使用。
20、python支持重载操作符,例如对加号+的重载如下:
class Complex: def __init__(self,real,imag=0): self.real = float(real) self.imag = float(imag) def __repr__(self): return "Complex(%s,%s)" % (self.real,self.imag) def __str__(self): return "(%g+%gj)" % (self.real,self.imag) def __add__(self,other): return Complex(self.real + other.real, self.imag + self.imag) def __sub__(self,other): return Complex(self.real - other.real, self.imag - self.imag)
21、检查一个实例是否属于某一类:isinstance(obj,name)
issubclass(A,B):如果类A属于类B,注意A和B都是cls,不是实例。
isinstance和issubclass都是可以重写的,这点上python比较灵活。
22、抽象类:Python支持抽象类,需要引用abc模块。
from abc import ABCMeta, abstractmethod, abstractproperty class Foo: __meta__ = ABCMeta #must 1 @abstractmethod #must 2 def spam(self,a,b): pass @abstractproperty #must 2 def name(self): pass
抽象类肯定是不能直接被实例化的,实现抽象类得方法并不是继承!而是注册,如下:
class Grok: def spam(self,a,b): print("Grok.spam") Foo.register(Grok)
23、Metaclass,我没看懂 - - 感觉是类似于Java的反射代理机制,用于框架时候比较给力。
24、类包装器Class Decorators: take a class as input and returns a class as output
其实也没太明白用途。。
#类包装器 registry = { } def register(cls): registry[cls.__clsid__] = cls return cls</pre> @register class Foo(object): __clsid__ = "123-456" def bar(self): pass #用法 register(Foo)