Python 面向对象

1 动态添加属性

对象使用过程中通过动态添加属性方便使用。


In [1]:
class Student(object):
    pass
bart = Student()
bart.name = 'gaufung'
bart.name


Out[1]:
'gaufung'

将强制性的属性作为类和对象的构造函数参数,__int__(self)


In [2]:
class Student(object):
    def __init__(self,name):
        self.name = name
bart = Student('zhang san')
bart.name


Out[2]:
'zhang san'

2 数据封装

以两个下划线 __开头的属性字段,为私有字段,在外部可以访问,通过getset操作完成属性修改。


In [5]:
class Student(object):
    def __init__(self,name):
        self.__name = name
    def get_name(self):
        return self.__name
bart = Student('gaofeng')
bart.__name


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-315f623a7981> in <module>()
      5         return self.__name
      6 bart = Student('gaofeng')
----> 7 bart.__name

AttributeError: 'Student' object has no attribute '__name'

In [6]:
bart.get_name()


Out[6]:
'gaofeng'

因为 Python 解释器将 __name 解释为 _Student__name 属性


In [7]:
bart._Student__name


Out[7]:
'gaofeng'

所以当选择将__name进行修改赋值的时候,相当于动态给bart对象动态添加了__name属性。


In [9]:
bart.__name='lisi'
bart.get_name()


Out[9]:
'gaofeng'

3 鸭子类型

3.1 继承和多态

在继承中在子类中通过相同的方法名,对父类中的方法进行override


In [16]:
class Animal(object):
    def run(self):
        print 'Animal is running'
class Dog(Animal):
    def run(self):
        print 'Dog is running'
class Cat(Animal):
    def run(self):
        print 'Cat is running'

def competition(animal):
    animal.run()
competition(Animal())
competition(Dog())
competition(Cat())


Animal is running
Dog is running
Cat is running

3.2 鸭子类型

与C#等动态语言不同的是,只要任何对象实现了run方法的对象都可以传给competition函数 看起来像鸭子,走起路来像鸭子,并不要严格的继承关系


In [17]:
class Chair(object):
    def run(self):
        print 'The chair will not run'
competition(Chair())


The chair will not run

4 类型判断

4.1 type()方法

type方法返回对象的类型


In [18]:
type(123)


Out[18]:
int

In [19]:
type('abc')


Out[19]:
str

In [20]:
type(None)


Out[20]:
NoneType

In [21]:
# 查看函数
type(abs)


Out[21]:
builtin_function_or_method

In [22]:
# 查看自定义对象
a = Animal()
type(a)


Out[22]:
__main__.Animal

4.2 isinstance 方法

通过isinstance方法可以查看类之间的继承关系。


In [23]:
b = Cat()
c = Dog()
isinstance(b,Animal)


Out[23]:
True

In [25]:
isinstance(b,Dog)


Out[25]:
False

In [26]:
# 能使用type的地方都可以改成isinstance
isinstance(123,int)


Out[26]:
True

In [27]:
isinstance('gau',str)


Out[27]:
True

4.3 操作对象属性


In [29]:
class MyObject(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x**2
obj = MyObject()
hasattr(obj,'x')


Out[29]:
True

In [30]:
hasattr(obj,'y')


Out[30]:
False

In [31]:
setattr(obj,'y',19)
hasattr(obj,'y')


Out[31]:
True

In [32]:
getattr(obj,'y')


Out[32]:
19

5 实例属性和类属性

在 Python 中,实例的属性用self关键字给出,而类的属性则不需要给出


In [34]:
class Student(object):
    name = 'student'
s = Student()
# 因为 s 没有 name 属性,因此向class方向查找
s.name


Out[34]:
'student'

In [36]:
Student.name


Out[36]:
'student'

In [37]:
s.name = 'gaufung'
s.name


Out[37]:
'gaufung'

In [38]:
Student.name


Out[38]:
'student'

6 动态绑定方法

Python 动态语言可以通过动态方式添加属性和方法


In [42]:
class Student(object):
    pass
def set_age(self,age):
    self.age = age
from types import MethodType
s1 = Student()
s1.set_age = MethodType(set_age,s1)
s1.set_age(25)
s1.age


Out[42]:
25

In [43]:
# 但是对一个对象动态绑定一个方法,对另一个对象并不起作用
s2 = Student()
s2.set_age(12)


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-43-d89b80e36e79> in <module>()
      1 # 但是对一个对象动态绑定一个方法,对另一个对象并不起作用
      2 s2 = Student()
----> 3 s2.set_age(12)

AttributeError: 'Student' object has no attribute 'set_age'

可以对整个类动态绑定一个方法


In [45]:
def set_score(self,score):
    self.score = score
Student.set_score = set_score
s1 = Student()
s2 = Student()
s1.set_score(100)
s1.score


Out[45]:
100

In [46]:
s2.set_score(99)
s2.score


Out[46]:
99

slots 方法

为了限制动态添加属性,可以通过__slots__方法限制属性的添加


In [47]:
class Student(object):
    __slots__=('name','age')
s = Student()
s.name = 'gaufung'
s.age = 25

In [48]:
s.grade = 'A'


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-48-14c8d8a6afe8> in <module>()
----> 1 s.grade = 'A'

AttributeError: 'Student' object has no attribute 'grade'

In [51]:
# 对于继承类并不起作用
class GraduateStudent(Student):
    pass
g = GraduateStudent()
g.grade = 'B'
g.grade


Out[51]:
'B'

7 定制方法

7.1 str


In [52]:
class Student(object):
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return "Student's name is {}".format(self.name)
    __repr__=__str__
Student('gaofeng')


Out[52]:
Student's name is gaofeng

In [54]:
print Student('gaufung')


Student's name is gaufung

7.2 iter

使得对象能够使用在for ... in结构中,在类中实现__iter__方法


In [59]:
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 # 返回下一个值

for i in Fib():
    print i,


1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025

7.3 getitem

通过索引的方式获取数据


In [64]:
class Fib(object):
    def __getitem__(self,n):
        a,b=1,1
        for i in range(n):
            a,b=b,a+b
        return a
Fib()[4]


Out[64]:
5

7.4 getattr

当获取一个不存在的属性时,程序会报错,而通过__getattr__方法指定相应属性可以获取属性


In [65]:
class MyClass(object):
    def __init__(self):
        self.age = 10
    def __getattr__(self,attr):
        if attr == 'score':
            return 'A'
obj = MyClass()
obj.age


Out[65]:
10

In [66]:
obj.score


Out[66]:
'A'

类似新浪微博web API的链式生成方式


In [67]:
class Chain(object):
    def __init__(self,path=''):
        self._path = path
    def __getattr__(self,path):
        return Chain('%s/%s'%(self._path,path))
    def __str__(self):
        return self._path
chain = Chain()
print chain.gau.fung.liu.bei


/gau/fung/liu/bei

7.5 call

使对象拥有函数的行为


In [68]:
class Student(object):
    def __init__(self):
        self.name = 'gaufung'
    def __call__(self):
        print 'Student name is {}'.format(self.name)
s = Student()
s()


Student name is gaufung

8 枚举类

通过常量的方式可以设置枚举类型,但总归是是常量表示


In [69]:
JAN = 1
FEB = 2
MAR = 3

更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能:


In [70]:
from enum import Enum
Week = Enum('Week',('Mon','Tue','Wed','Thu','FRI','SAT','SUN'))
for name,member in Week.__members__.items():
    print name,'=>',member,',',member.value


Mon => Week.Mon , 1
Tue => Week.Tue , 2
Wed => Week.Wed , 3
Thu => Week.Thu , 4
FRI => Week.FRI , 5
SAT => Week.SAT , 6
SUN => Week.SUN , 7

精确控制枚举类型,使用类


In [71]:
from enum import Enum,unique
@unique
class Week(Enum):
    Mon = 0 
    Tue = 1
    Wed = 2
    Thu = 3
    Fri = 4
    Sat = 5
    Sun = 6
day1 = Week.Mon
print(day1)


Week.Mon

In [73]:
day1.value


Out[73]:
0

In [ ]: