廖雪峰Python教程阅读笔记——6. 面向对象编程

来源:互联网 发布:linux fg命令 编辑:程序博客网 时间:2024/06/06 17:18

6.面向对象编程

OOP是一种设计思想,OOP把对象作为程序的基元。一个对象包括属性和操作属性的方法。
在Python中,所有的对象都可视为对象,当然也可以自定义对象。自定义的对象数据类型,就是面向对象中的类(class)。
设想打印学生的成绩,学生有姓名和成绩两种属性。我们创建一个学生对象Student,有姓名name和成绩score两个属性。如果要打印学生的成绩,必须创建出一个学生对象出来,然后使用print_score,让对象把自己的数据打印出来。

'测试学生对象'class Student(self, name, score):    '学生类'    def __init__(self):        '初始化对象,类似于构造函数'        self.name = name        self.score = score    def print_score(self):        '打印输出'        print("%s, %s" % (self.name, self.score))

6.1类和实例

面向对象的最重要的概念就是类(class)和实例(instance)。在Python中用关键字class紧跟类名,来定义类。定义好了类,可以使用类名+()来实例化:

>>>bart = Student('Minie',69)

该语句,是创建一个student对象,并将变量bart指向该对象。
注意到,上面定义的类,__init__方法第一个参数是self,表示创建实例本身。有了__init__方法,实例化对象时,就不能不传参数了,必须传入与__init__方法匹配的参数。类似于java的构造函数。
和普通函数相比,在类中定义的函数,第一个参数永远是实例变量self,并且调用时,不需要传该参数。除此之外,类的方法和普通函数没有区别。
- 数据封装
如上述的例子,student对象,在类中,定义了一些数据姓名name和分数score。这些数据的函数是和student类本身关联起来的。

小结:类是创建实例的模板,而实例是一个一个的具体的对象,各个实例拥有的数据都互相独立。方法就是与实例绑定的函数,与普通函数不同,方法可以直接操作类中的数据。

6.2访问限制

在class内部,可以有属性和方法,而外部代码可以直接调用实例来操作数据。但是从前面定义的Student类来看,外部代码还是可以自由的修改实例的name、score属性:

>>>bart = Student('Bartun',90)>>>bart.score90>>>bart.score=80>>>bart.score80

如果要让内部属性不被外部改变,则把属性前面加两个_,在Python中,实例的变量如果用__开头,就变成了一个私有变量,只有内部可以访问,外部不能访问。
我们把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))

此时,无法通过外部访问内部变量__name__score。如果想要访问,就必须加getset方法

注意:在Python中,变量名类似__XXX__开头和结尾都是__的,说明是特殊变量。特殊变量时可以直接访问的,不是Private的。有时候类似_xxx这种变量,这种变量虽然可以被直接访问,但是按照约定的规则,视此类型的变量为私有变量,不要随意访问。

6.3继承和多态

在OOP程序中,我们定义了一个类(class),该类可以被继承。
定义一个Animal类,有run()方法和eat()方法,再定义一个cat和dog类,也有run方法。当子类和父类都有相同的方法时,子类方法覆盖掉父类方法,这种现象称为多态。
在继承关系中,子类的数据类型是属于父类的数据类型。

动态语言 VS 静态语言
对于类的继承,如果是静态语言(Java),继承时传入的类型必须是父类型或者其子类。否则无法调用run()方法。
对于Python类型,不一定是父类型,只要保证传入的类型中有run()方法就行。这就是Python的”file-like object”鸭子类型。

小结:
继承可以把父类的所有功能继承下来,也可以重载。动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

6.4获取对象信息

当我们拿到一个对象的引用时,如何知道这个对象是什么类型,有哪些方法?

  • 获取类型type()
    通过type()可以获取对象的类型:
>>> type(123)<class 'int'>>>> type('ab')<class 'str'>>>> import math                                      >>> type(abs)     ##获取函数的类型<class 'builtin_function_or_method'>

可以通过是否相等,判断两个对象的类型是否一致,如果对于继承关系的对象,最好用isinstance()函数。
- 获取方法dir()
如果要获取一个对象的所有属性和方法,可以使用dir()函数。它返回一个包含所有字符串的list。

>>> dir('abc')                                                                                                                              ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']                                                           

其中,不乏有__XXX__的属性和方法,这在Python中是有特殊用途的,比如__len__方法返回长度。在Python中,你调用了len()函数,会返回一个对象的长度。实际上,在lne()内部,它会自动调用该对象的__len__方法。
下面代码是等价的:

>>> len('abc')                                        3                                                   >>> 'abc'.__len__()                                                   3 

剩下的都是普通属性和方法。
再配合getattr()setattr()hasattr()可以直接操作一个对象的状态。

小结:
通过一系列内置函数,我们可以对任意一个对象进行剖析,但是只有在不知道对象信息的时候,我们才会获取对象信息。

6.5实例属性和类属性

略.

原创粉丝点击