4.1 对象
1.类
在日常生活中,我们把相近的一类归为一类,并且给这一类取一个类名,比如鸟类的共同属性是有羽毛,通过产卵生育后代:
在这里,我们用‘class’来定义一个类,类名就是‘Bird’,括号里的关键词‘object’即某一个个体,在计算机语言中,我们称一个个体为对象。一个类中可以有各式各样的多个个体,鸟类可以包括各式各样的鸟:
。。。。。。
冒号和缩进说明了属于这个类的代码。在例子类中我们定义了两个属性:一个用于说明鸟类有羽毛(feather),一个用于说明鸟类的繁殖方式(reproduction)。
除了用数据的属性来分辨类别外,有时也根据这类东西能做什么事情来区分,比如鸟会移动,鸟的移动会导致位置的变化,这样的一些[行为]属性称为方法(method)。在python中,一般通过类内部函数来定义和说明方法。
函数chirp(self,sound)中,第一个参数为‘self’,这是为了在方法内部引用对象自身,需要强调的是,无论该参数是否用到,方法的第一个参数必须是用于指代对象自身的self。剩下的参数sound是为了满足我们的需求设计的,它代表了鸟类的叫声的内容。
2.对象
通过调用类,我们可以创造出一个新的物体——对象。
例如我有一只小鸡取名叫Neo,他是个对象,且属于鸟类,我们可以使用前面定义好的鸟类,产生这个对象:
在调用方法时,我们只传递了一个参数,也就是字符串‘meow’,虽然在定义方法时我们必须加上self形参,但这个self只能用在类定义的内部,所以在调用方法时,不需要对self进行赋值。
到现在为止,描述对象的数据都存储于类的属性中。类属性描述了一个类的共性,比如鸟类都有羽毛,所有属于这类的对象都会共享这些属性。
但是对于类中的一个对象来说,会存在个体差异,因此,为了完整描述个体,除了共有的类属性外,我们还需要描述个体属性的——【对象属性】。
在方法set_color()中,我们通过self参数设定了对象的属性color,我们能通过对象.属性的方式来操作对象属性。由于对象属性依赖于self,所以我们必须在某个方法内部才能操作类属性,对象属性没有办法像类属性一样,在类下方直接赋予初始值。
python提供了初始化对象属性的办法,我们称之为魔法方法-magicmethod。魔法方法的方法名很特殊,前后有两个下划线,例如:__init__(),__add__(),__dict__()等。
对于类的_init_()方法,python会在每次创建对象时自动调用:
在上面的类定义中,我们通过__init__()方法说明了这个类的初始化方式,每当对象建立时,比如创建Neo时,__init__()方法就会被调用,它会设定sound这个对象的属性,在后面的chirp()方法中,就能通过self参数调用这个对象/实例属性。除了设定对象属性外,我们还可以在__init__()中加入其他指令。
除了操作对象属性外,self参数还有另一个功能,就是让我们在一个方法内部调用同一类的其他方法:
在chirp_repeat()中,我们通过self参数调用了类中的另一个方法chirp()。
4.2 继承者
1.子类
在面对对象的编程中,我们把大类细分成小类的概念称为继承(Inheritance)。
在定义小类时,括号里的Bird说明新定义的小类Chicken以及Swam都是属于Bird这个大类,两个小类享有大类的所有属性。
2.属性覆盖
在继承的过程中,我们可以增加在父类中没有的属性,也可以修改源自父类的属性:
Chicken是Bird的子类,在Chicken中我们也定义了方法chirp(),通过调用可以看到,Chicken会调用自身定义的chirp方法,而不是父类Bird中的chirp方法,这样就像覆盖了父类方法一样。
通过对方法的覆盖我们可以彻底改变子类的行为,但有时子类行为是父类行为的拓展,这时可以用super关键字在子类中调用父类中被覆盖的方法:
通过super指令,我们在子类同名方法中调用了父类的方法,这样的目的是使子类既可以执行父类中的相关操作,又能定义属于自己的额外操作。
4.3 对象种类
1.列表对象
当我们新建一个新列表时,实际上是python在列表类中创建了一个列表对象。这种由python提前定义好的大类被称为内置类。由于想知道的更加细腻,我们使用dir()和help()来查询详细信息:
2.元组与字符串对象
由于元组属于定值表,所以不能进行修改操作,我们只能执行查询操作:
字符串是一种特殊的元组,因此可以执行元组的方法:
尽管字符串是一种特殊的元组,但它可以执行一些改变操作,其实这是删除了源字符串,建立了一个新字符串,所以并没有违背元组的不可变性。
字符串的查询及详细信息可以使用help()文档进行学习。在此不做赘述。
3.词典对象
词典同元组/列表一样,是一个类。
通过词典的keys()方法来循环遍历词典中的每个元素或者键值:
通过values()方法,可以遍历每个元素的值,或items()方法直接遍历每个元素:
也可以使用clear()方法,清空整个词典:
4.4 特殊对象
1.循环对象
概念:所谓的循环对象就是包含一个__next__()方法,这个方法的目的就是生成循环的下一个结果。在生成所有的结果之后,改方法将抛出StopIteration异常。
过程:当一个像for循环语法调用循环对象时,它会在每个循环的时候调用__next__()方法,直到StopIteration出现。循环接到这个这个异常就会知道循环已经结束,将停止调用__next__()。
使用iter()函数可以返回一个循环/迭代器对象:
这就是手动重复调用__next__()的过程,相当于进行了手动循环:
在这里,for结构将自动调用__next__()方法,也可省略iter()函数的转换,因为for将自动执行这一转换。
对比序列,循环对象的好处在于,不用在循环开始之前就要生成要使用的元素,所有要使用的元素可以在循环的过程中逐渐生成。这样会更加高效,省空间。
如何自定义一个循环对象呢?这时需要借助生成器:generator来自定义一个循环对象,生成器的编写和函数类似,只是在return的位置改为yield,当生成器运行遇到一个yield时,会暂停运行生成器,返回yield后面的值,当再次调用生成器时,才会从暂停的地方开始继续运行,直到下一个yield。
2.函数对象
在python中,函数也是一种对象,实际上,任何一个含有__call__()特殊方法的对象都被当做是函数:
3.模块对象
在python中,一个模块对应一个.py文件,模块对象正是如此。
比如直接引入标准模块中的time模块:
运行后,经过10秒将打印‘wakeup’。
time模块的sleep()会中止程序,参数代表中止的时间。
除了精细的引入方法,我们还可以使用粗暴简单的全局引入:
既然知道了sleep()是time模块下的一个方法,我们能用对象.属性的方式来调用它:
我们在调用方法时附带上了对象名,这样做是为了避免同名冲突,假如两个模块内都有sleep()方法,那么我们可以通过不一样的模块名来进行区分:
在同一目录下定义一个新模块my_time.py,写入:
新建一个python文件main.py,在main.py中引入:
上面两次对sleep()的调用我们通过对象名区分出了不同的sleep()方法。
引入模块时,我们还能改变模块的命名:
在引入模块名过长时这个方法可以节省事件提高效率。