python学习记录七
面向对象编程
类和实例
类是抽象的模板
class Student(object): passclass
后面紧接着是类名,即Student
,类名通常是大写开头的单词,紧接着是(object)
,
表示该类是从哪个类继承下来的,继承的概念我们后面再讲,
通常,如果没有合适的继承类,就使用object
类,这是所有类最终都会继承的类
创建实例 是通过类名+()实现的
>>> bart = Student() >>> bart <__main__.Student object at 0x10a67a590> #表示的是bart指向的就是一个Student的实例,后面的0x10a67a590是内存地址 每一个实例的内存地址是不一样的 >>> Student <class '__main__.Student'>
可以自由的给一个实例变量绑定属性 比如说 给实例bart绑定一个name属性
>>> bart.name = 'Bart Simpson' >>> bart.name 'Bart Simpson'
把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__
方法
class Student(object) def __init__(self, name, score): self.name = name self.score = score
注意:特殊方法“__init__”前后分别有两个下划线!!
注意到__init__
方法的第一个参数永远是self
,表示创建的实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身。
有了__init__
方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__
方法匹配的参数,但self
不需要传,Python解释器自己会把实例变量传进去:
数据封装
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)) 要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入:
访问限制
在class内部,可以有属性和方法 而外部代码可以通过内部提挨冻实例变量的方法哎操作隐藏了内部的复杂逻辑
但是从前面的Student类的定义来看,外部代码还是可以自由的修改一个实例的name,score属性
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
,在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:
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__
这样的变量名。
最后注意下面的这种错误写法:
>>> bart = Student('Bart Simpson', 59) >>> 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
继承和多态
class Animal(object): def run(self): print('Animal is running...')
当我们需要编写Dog
和Cat
类时,就可以直接从Animal
类继承:
class Dog(Animal): pass class Cat(Animal): pass
当然,也可以对子类增加一些方法,比如Dog类: class Dog(Animal): def run(self): print('Dog is running...') def eat(self): print('Eating meat...')
当子类和父类都存在相同的run()
方法时,我们说,子类的run()
覆盖了父类的run()
,在代码运行的时候,总是会调用子类的run()
。这样,我们就获得了继承的另一个好处:多态。
静态语言 vs 动态语言
对于静态语言(例如Java)来说,如果需要传入Animal
类型,则传入的对象必须是Animal
类型或者它的子类,否则,将无法调用run()
方法。
对于Python这样的动态语言来说,则不一定需要传入Animal
类型。我们只需要保证传入的对象有一个run()
方法就可以了:
class Timer(object): def run(self): print('Start...')
这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()
方法,返回其内容。但是,许多对象,只要有read()
方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()
方法的对象。