python初心记录三

面向对象


类是建立实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;html

方法就是与实例绑定的函数,和普通函数不一样,方法能够直接访问实例的数据;python

经过在实例上调用方法,咱们就直接操做了对象内部的数据,但无需知道方法内部的实现细节。编程

和静态语言不一样,Python容许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不一样实例,但拥有的变量名称均可能不一样:网络

>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8
>>> bart.age
8
>>> lisa.age
Traceback (most recent call last):
  File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'age'

访问限制

对象中使用双下划线__开头设置私有变量,外部只能够经过_类名__变量访问,可是不建议这样作。函数

须要注意的是,在Python中,变量名相似__xxx__的,也就是以双下划线开头,而且以双下划线结尾的,是特殊变量,特殊变量是能够直接访问的,不是private变量,因此,不能用__name____score__这样的变量名。ui

有些时候,你会看到以一个下划线开头的实例变量名,好比_name,这样的实例变量外部是能够访问的,可是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我能够被访问,可是,请把我视为私有变量,不要随意访问”。spa

双下划线开头的实例变量是否是必定不能从外部访问呢?其实也不是。不能直接访问__name是由于Python解释器对外把__name变量改为了_Student__name,因此,仍然能够经过_Student__name来访问__name变量:设计

>>> bart._Student__name 'Bart Simpson' 

可是强烈建议你不要这么干,由于不一样版本的Python解释器可能会把__name改为不一样的变量名。code

总的来讲就是,Python自己没有任何机制阻止你干坏事,一切全靠自觉。xml

最后注意下面的这种错误写法:

>>> bart = Student('Bart Simpson', 98) >>> bart.get_name() 'Bart Simpson' >>> bart.__name = 'New Name' # 设置__name变量! >>> bart.__name 'New Name' 

表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改为了_Student__name,而外部代码给bart新增了一个__name变量。不信试试:

>>> bart.get_name() # get_name()内部返回self.__name 'Bart Simpson'

继承和多态

在OOP程序设计中,当咱们定义一个class的时候,能够从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

继承能够把父类的全部功能都直接拿过来,这样就没必要重零作起,子类只须要新增本身特有的方法,也能够把父类不适合的方法覆盖重写。

动态语言的鸭子类型特色决定了继承不像静态语言那样是必须的。

获取对象信息

经过内置的一系列函数,咱们能够对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,咱们才会去获取对象信息。若是能够直接写:

sum = obj.x + obj.y 

就不要写:

sum = getattr(obj, 'x') + getattr(obj, 'y') 

一个正确的用法的例子以下:

def readImage(fp): if hasattr(fp, 'read'): return readData(fp) return None 

假设咱们但愿从文件流fp中读取图像,咱们首先要判断该fp对象是否存在read方法,若是存在,则该对象是一个流,若是不存在,则没法读取。hasattr()就派上了用场。

请注意,在Python这类动态语言中,根据鸭子类型,有read()方法,不表明该fp对象就是一个文件流,它也多是网络流,也多是内存中的一个字节流,但只要read()方法返回的是有效的图像数据,就不影响读取图像的功能。

实例属性和类属性

因为Python是动态语言,根据类建立的实例能够任意绑定属性。

编写程序的时候,千万不要把实例属性和类属性使用相同的名字,由于相同名称的实例属性将屏蔽掉类属性,可是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

面向对象高级编程

数据封装、继承和多态只是面向对象程序设计中最基础的3个概念。在Python中,面向对象还有不少高级特性,容许咱们写出很是强大的功能。

咱们会讨论多重继承、定制类、元类等概念。

使用__slots__

可是,若是咱们想要限制实例的属性怎么办?好比,只容许对Student实例添加nameage属性。

为了达到限制的目的,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 

除非在子类中也定义__slots__,这样,子类实例容许定义的属性就是自身的__slots__加上父类的__slots__

使用@property

@property普遍应用在类的定义中,可让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减小了出错的可能性。

class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2015 - self._birth

多重继承

经过多重继承,一个子类就能够同时得到多个父类的全部功能。



pass # 大类: class Mammal(Animal): pass class Bird(Animal): pass # 各类动物: class Dog(Mammal): pass class Bat(Mammal): pass class Parrot(Bird): pass class Ostrich(Bird): pass

MixIn

在设计类的继承关系时,一般,主线都是单一继承下来的,例如,Ostrich继承自Bird。可是,若是须要“混入”额外的功能,经过多重继承就能够实现,好比,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计一般称之为MixIn。

为了更好地看出继承关系,咱们把RunnableFlyable改成RunnableMixInFlyableMixIn。相似的,你还能够定义出肉食动物CarnivorousMixIn和植食动物HerbivoresMixIn,让某个动物同时拥有好几个MixIn:

class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass

定制类

Python的class容许定义许多定制方法,可让咱们很是方便地生成特定的类。

本节介绍的是最经常使用的几个定制方法,还有不少可定制的方法,请参考Python的官方文档

使用枚举类

Enum能够把一组相关常量定义在一个class中,且class不可变,并且成员能够直接比较。

@unique装饰器能够帮助咱们检查保证没有重复值。

访问这些枚举类型能够有若干种方法:

>>> day1 = Weekday.Mon >>> print(day1) Weekday.Mon >>> print(Weekday.Tue) Weekday.Tue >>> print(Weekday['Tue']) Weekday.Tue >>> print(Weekday.Tue.value) 2 >>> print(day1 == Weekday.Mon) True >>> print(day1 == Weekday.Tue) False >>> print(Weekday(1)) Weekday.Mon >>> print(day1 == Weekday(1)) True >>> Weekday(7) Traceback (most recent call last): ... ValueError: 7 is not a valid Weekday >>> for name, member in Weekday.__members__.items(): ... print(name, '=>', member) ... Sun => Weekday.Sun Mon => Weekday.Mon Tue => Weekday.Tue Wed => Weekday.Wed Thu => Weekday.Thu Fri => Weekday.Fri Sat => Weekday.Sat 

可见,既能够用成员名称引用枚举常量,又能够直接根据value的值得到枚举常量。

使用元类

metaclass是Python中很是具备魔术性的对象,它能够改变类建立时的行为。这种强大的功能使用起来务必当心。

相关文章
相关标签/搜索