6、面向对象编程

面向对象编程 #

类和实例 #

class Student(object):
    pass

>>> bart = Student()
# 可以自由地给一个实例变量绑定属性
>>> bart.name = 'Bart Simpson'
>>> bart.name
'Bart Simpson'

class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

class Student(object):
    #通过定义一个特殊的`__init__`方法,在创建实例的时候,就把`name`,`score`等属性绑上去
    def __init__(self, name, score):
        self.name = name
        self.score = score
        
>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59

注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

class Student(object):
    name = ''

    def __init__(self, name, score):
        self.name = name
        self.score = score

    # 定义方法
    def print_score(self):
        print('%s: %s' % (self.name, self.score))
        
>>> bart.print_score()
Bart Simpson: 59

访问限制 #

class Student(object):

    # 属性的名称前加上两个下划线`__`,就变成了一个私有变量
    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name____score__这样的变量名。

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

继承和多态 #

class Animal(object):
    def run(self):
        print('Animal is running...')
        
class Dog(Animal):
    # pass空内容占位
    pass

class Cat(Animal):
    pass

dog = Dog()
dog.run()

cat = Cat()
cat.run()

class Dog(Animal):

    def run(self):
        print('Dog is running...')

class Cat(Animal):

    def run(self):
        print('Cat is running...')

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。

# 定义一个class的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样
a = list() # a是list类型
b = Animal() # b是Animal类型
c = Dog() # c是Dog类型

>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True

#多态的好处
def run_twice(animal):
    animal.run()
    animal.run()

>>> run_twice(Animal())
Animal is running...
Animal is running...

>>> run_twice(Cat())
Cat is running...
Cat is running...

#新增一个`Animal`的子类,不必对`run_twice()`做任何修改,实际上,任何依赖`Animal`作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
class Tortoise(Animal):
    def run(self):
        print('Tortoise is running slowly...')

>>> run_twice(Tortoise())
Tortoise is running slowly...
Tortoise is running slowly...

获取对象信息 #

# 判断对象类型
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>

>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>

>>> type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False

#判断一个对象是否是函数
>>> import types
>>> def fn():
...     pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True

#`isinstance()`判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。
>>> isinstance(h, Dog)
True

#能用`type()`判断的基本类型也可以用`isinstance()`判断
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True

#判断一个变量是否是某些类型中的一种
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

# 获得一个对象的所有属性和方法,可以使用`dir()`函数
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
...
>>> obj = MyObject()
>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19
>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
404
>>> hasattr(obj, 'power') # 有属性'power'吗?
True
>>> getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81

实例属性和类属性 #

    # 给实例绑定属性的方法是通过实例变量,或者通过`self`变量
    class Student(object):
        def __init__(self, name):
            self.name = name
    
    s = Student('Bob')
    s.score = 90
    
    # 直接在class中定义属性,这种属性是类属性,归`Student`类,类的所有实例都可以访问到
    class Student(object):
        name = 'Student'