Python学习笔记5

Python学习笔记–5.面向对象编程

MrZigZag @ Peking University

2015-07-17


1.定义

通过class关键字定义类,父类(object)

class MyClass(object):
    pass

没有合适的继承类时可以使用object类,这是所有的类都会继承的类。

自由绑定,Python是动态语言,可以在运行过程中实时地给实例instance绑定新的属性,同时还可以绑定新的方法。

>>> ins1 = MyClass()
>>> ins1.name = 'Test'

def set_age(self, age):
    self.age = age
from types inport MethodType
s = Student()
s.set_age = MethodType(set_age, s, Student)

方法,类的初始化方法是__init__(self, args),任何方法第一个参数都是self,但是使用时不需要传进去。

2.访问限制

属性名称加上__表示private,__prop会被解释器解释成_Classname__prop,要访问__prop可以写__Classname__prop

双下划线开头双下划线结尾的属性属于特殊变量,可以访问,一般不能使用这个命名方法。

单下划线开头的变量可以访问,但是一般被视为private变量。

3.继承和多态

继承之后,子类的方法会覆盖父类的方法。

多态保证了只需要知道父类,并不需要确切的子类就可以放心的使用父类中的方法。因而调用方只管调用不管细节。符合“开闭原则”。对扩展开放,对修改封闭

class Animal(object):
    def run(self):
        print 'Animal is running!'
class Dog(Animal):
    def run(self):
        print 'Dog is runnning!'
def run_twice(animal):
    animal.run()
    animal.run()

>>> run_twice(Animal())
Animal is running!
Animal is running!
>>> run_twice(Dog())
Dog is running!
Dog is running!

根据多态的性质,任何时候最好都要使用继承,如果没有合适的父类则直接继承object

4.获取对象信息

拿到一个对象的引用时,需要确定对象的类型和包含的方法。可以使用多种方法。

使用type(),基本类型都可以使用type()函数判断。

>>> type(1)
<type 'int'>
>>> type('hello')
<type 'str'>
>>> type(None)
<type 'NoneType'>
>>> type(abs)
<type 'builtin_function or method'>
>>> type(Animal())
<type '__main__.Animal'>

type()函数返回type类型,Python把每种type类型都定义好了常量,放在types模块中。

>>> import types
>>> type('abc') == types.StringType
True
>>> type(u'abc') == types.UnicodeType
True
>>> type([]) == types.ListType
True
>>> type(int) = types.TypeType

不同类型都有对应的type,所有类型本身的类型就是一种TypeType

使用isinstance(),判断class的类型使用type()并不是很方便,直接使用isinstance()函数。

>>> a = Ainimal()
>>> d = Dog()
>>> isinstance(d, Dog)
True
>>> isinstance(d, Animal)
True

可见isinstance()对继承关系也是有用的。当然,isinstance()还可以判断变量是某些类型中之一。

>>> isinstance('a', str)
True
>>> isinstance('a', (str, unicode))
True

使用dir()可以获得一个对象的所有属性和方法。返回一个包含字符串的list。

同时配合getattr()setattr()hassttr()可以直接操作一个对象的状态。

>>> hasattr(obj, 'x')
True
>>> hasattr(obj, 'y')
False
>>> setattr(obj, 'y', 10)
>>> getattr(obj, 'y')
10
>>> obj.y
10
>>> getattr(obj, 'z', 404)
404

一般正确的用法:

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

5.slots

基于Python的动态语言特性,创建class实例之后我们还可以在运行过程中给该实例绑定新的属性和方法,但是新绑定的属性和方法对其他实例是不起作用的。为了给所有实例都绑定方法,可以直接给class绑定新的方法。

def set_score(self, score):
    self.score = score;
Student.set_score = Method_Type(set_score, None, Student)

使用slots可以限制class的属性。Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class能添加的属性。__slots__仅仅对当前class有效,对继承的子类是不起作用的。

class Student(object):
    __slots__ = ('name', 'age') # Use tuple to describe __slots__

如果在子类中定义__slots__,那么子类允许定义的属性就是自身的__slots__加上父类的__slots__

6.@property

绑定属性时,为了方便起见,有时候会将属性直接暴露出去,但是这样就没法检查参数,导致属性可以随便改。因此,我们需要限制属性的范围。

class Student(object):
    def get_score(self):
        return self._score
    def set_score(self, val):
        if not isinstance(val, int):
            raise ValueError('score must be an integer')
        if val < 0 or val > 100:
            raise ValueError('score must between 0 - 100')
        self._score = val

但是这样做就没法将属性直接暴露出去方便读写了。因此我们可以用装饰器decorator给函数动态加上功能。Python内置的@property装饰器就是负责把一个方法变成属性调用。把一个getter方法变成属性只需加上@property,此时@property本身又创建了另一个装饰器@property.setter,负责把setter方法编程属性直接赋值。

class Student(object):
    @property
    def score(self):
        return self._score
    @property.setter
    def score(self, val):
        if not isinstance(val, int):
            raise ValueError('score must be an integer')
        if val < 0 or val > 100:
            raise ValueError('score must between 0-100')
        self._score = val

这样可以把score当做public的属性来用。

>>> s = Student()
>>> s.score = 100 # equals s.score(100)
>>> s.score # equals s.score()
100

当只使用@property而不使用@property.setter时相关的变量就是只读属性

7.多重继承