标签:python
Python是面向对象的语言,Python中的类提供了面向对象的所有特征:多态、封装和继承。更多面向对象的概念请参考专门的面向对象的书籍,下面将介绍Python中类的定义和使用方法。
下面是定义一个类的最简单的形式:
class ClassName: <statement-1> . . . <statement-N>类就像函数一样,在使用之前必须先定义(支持将类的定义放在if语句的分支,或者函数内部)。
类对象支持两种操作:属性引用和实例化。
属性引用使用那个标准语法:obj.name。当类被创建时,在类的命名空间中的所有名称都是有效的属性名。例如,如果类定义如下:
class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world'那么MyClass.i和MyClass.f是有效的属性引用,分别返回一个整数和一个函数对象。类属性也可以分配,因此你能修改被分配的MyClass.i的值。__doc__也是一个有效的属性,返回类的文档字符串,即:"A simple example class"。
x = MyClass()这里创建一个类的新实例并分配这个对象给本地变量x。
def __init__(self): self.data = []当类定义了__init__()方法,类实例化会自动调用__init__()。在这个例子中,新实例能通过下面的方法获取,并拥有了一个data属性:
x = MyClass()__init__()也可以传入参数,参数通过类的实例化操作传给__init__(),例如:
>>> class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart >>> x = Complex(3.0, -4.5) >>> x.r, x.i (3.0, -4.5)
接下来我们能对实例对象做什么操作呢?实例对象仅支持属性引用,有两种有效的属性名:数据属性和方法。
数据属性对应Smalltalk中的“实例变量”和C++中的“数据成员”。数据属性不需要被定义,同本地变量,当第一次被使用时被自动定义。例如,假定x是上面定义的MyClass的实例,下面的代码将打印16:
x.counter = 1 while x.counter < 10: x.counter = x.counter * 2 print(x.counter) del x.counter实例对象的另一种属性引用是方法,一个方法是一个属于对象的函数。
通常,一个方法使用下面的方法调用:
x.f()
xf = x.f while True: print(xf())当一个方法被调用时将发生什么呢?你可能已经注意到,虽然f()在定义时指定了一个参数,但调用时却不需要传入参数,什么原因呢?当一个函数需要一个参数,调用时却没有传入,Python将抛出一个异常,这一点是确定的。你也许已经猜到,方法的第一个参数就是对象本身,在我们的例子中,调用x.f()等价于调用MyClass.f(x),通常,调用一个n个参数的方法等价于调用一个带有同样n个参数的函数,并在第一个参数前插入方法所属的对象。
通常来讲,实例变量对应每个特定实例,而类变量对应所有类的实例:
class Dog: kind = 'canine' # class variable shared by all instances def __init__(self, name): self.name = name # instance variable unique to each instance >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.kind # shared by all dogs 'canine' >>> e.kind # shared by all dogs 'canine' >>> d.name # unique to d 'Fido' >>> e.name # unique to e 'Buddy'这里Dog.kind是类变量,而name则是实例变量,类变量可以在类的不同实例间共享,而实例变量则对应到类的每一个实例。
class Dog: tricks = [] # mistaken use of a class variable def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks # unexpectedly shared by all dogs ['roll over', 'play dead']由于类变量tricks可以被类的所有实例共享,所以list中的数据将是不可预测的,导致可能引发一些奇怪的行为,正确的做法应该是为每一个实例对象提供一个tricks变量:
class Dog: def __init__(self, name): self.name = name self.tricks = [] # creates a new empty list for each dog def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks ['roll over'] >>> e.tricks ['play dead']
如果类的数据属性和方法属性具有相同的名称,在大的程序中可能引发一些很难发现的bug。因此,为了避免名字冲突,使用一些命名的约定是有必要的,可以尽量的减少冲突。可能的约定包括大写方法名称、 使用某个小的唯一字符串(也许只是一条下划线)作为数据属性名称的前缀、或使用动词名词对于数据的属性和方法。
数据属性可以被对象的使用者引用,也可以被方法引用。换句话说,类无法实现纯粹的抽象数据类型,实际上,Python无法实现数据隐藏,所有的都是基于约定。
对数据属性的使用应该非常小心,如果你修改了变量的值,可能会影响依赖这个变量的方法的执行。用户还可以为实例对象添加自己的数据属性,但需要避免名称冲突,因此,命名约定可以减少大量令人头痛的问题。
通过方法来引用数据属性往往可以增加可读性,用户不容易混淆本地变量和实例变量。
方法的第一参数通常是self,但这仅仅是一个惯例,但是,遵守这个惯例往往会让其它Python编程人员更容易阅读你的代码,也容易通过编辑器自动生成。
函数对象并不一定要定义在类定义中,先定义一个函数,然后将其分配给类的本地变量也是可以的,例如:
def f1(self, x, y): return min(x, x+y) class C: f = f1 def g(self): return 'hello world' h = g现在f、g和h都是类C的属性了,执行一个函数对象,并且都将成为C的实例的方法(h和g是完全等同的)。但是,这个在实践中会导致代码的可读性差,不会带来实际好处。
class Bag: def __init__(self): self.data = [] def add(self, x): self.data.append(x) def addtwice(self, x): self.add(x) self.add(x)方法也可以引用全局名称(变量或者函数),需要在引用前引入包含该名称定义的模块。
>>> a = 1 >>> a.__class__ <class 'int'> >>> a = 1.1 >>> a.__class__ <class 'float'> >>> class Bag: def __init__(self): self.data = [] def add(self, x): self.data.append(x) def addtwice(self, x): self.add(x) self.add(x) >>> Bag.__class__ <class 'type'>
Python中派生类的定义语法如下:
class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N>aseClassName是基类的名称,也支持在基类的名称前加模块前缀,例如:
class DerivedClassName(modname.BaseClassName):派生类定义的执行处理和基类是一样的,当类对象被构建,基类被保存。当类中请求的属性没有找到时,则在基类中搜索,一直向上,直到到达最上层的基类。
Python支持多重继承,一个类可以定义多个基类:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>简单来讲,在多重继承下,类的属性的搜索采用深度遍历、从左到右的顺序。因此,如果一个属性在DerivedClassName中没有,则会搜索Base1,再搜索Base1的基类,如果没有发现,则搜索Base2,等等。
在Python中不存在私有变量,但是,存在一个被大多数Python代码遵守的惯例:以‘_‘为前缀的命名应该被作为API的非公共部分(无论是函数、方法或者数据成员),它应该在不需要通知的情况下就可以被修改。
Python为类元素的私有性提供初步的形式,由双下划线开始的属性(实际上是至少两个下划线开头,之多一个下划线结尾)在运行时被混淆,不能够直接访问,例如:
>>> class Test: __a_ = "This is a test." >>> t = Test() >>> t.__a_ Traceback (most recent call last): File "<pyshell#71>", line 1, in <module> t.__a_ AttributeError: 'Test' object has no attribute '__a_'混淆后的名称前会加上下划线和类名,即_classname__。因此,类Test中的变量__a_被混淆后变为_Test__a_:
>>> t._Test__a_ 'This is a test.'这样做提供了某种层次上的私有化,名字混淆的另一个目的,是为了保护__XXX变量不与父类名字空间相冲突,并且父类的__XXX变量不会被子类覆盖。例如:
>>> class Test: def test(self): print("this is base.") __test = test def doTest(self): self.__test() >>> class TestSubclass(Test): def test(self): print("this is subclass.") >>> a = Test() >>> b = TestSubclass() >>> a.test() this is base. >>> a.doTest() this is base. >>> b.test() this is subclass. >>> b.doTest() this is base.
标签:python
原文地址:http://blog.csdn.net/tomato__/article/details/45500833