python从入门到实践第九章_《Python编程:从入门到实践》(第九章)

第九章(类的学习)

《Python编程:从入门到实践》这一系列总结都是围绕python 3.X版本以上进行总结,不考虑2.7版本。

1.1基本概念

面向对象编程是最有效的软件编写方法之一。

面向对象编程:你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。编写类时,你定义一大类对象都有的通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。

编程模式历程:面向过程编程-->面向函数编程-->面向对象编程

根据类来创建对象被称为实例化,这让你能够使用类的实例。

1.1.1类的作用

类的基本作用:你将指定可在实例中存储什么信息,定义可对这些实例执行哪些操作(类中定义公共,实例定义私有)

进阶使用(类的继承):编写一些类来扩展(继承既有类)既有类的功能,让相似的类能够高效地共享代码。

模块化:你将把自己编写的类存储在模块中,并在自己的程序文件中导入其他程序员编写的类。

1.2类的基本使用

使用类几乎可以模拟任何东西。注意:类表示的不是特定的事物,而是泛指这一事物。

比如狗就是一个类,而具体的狗:哈士奇,旺财...特指的就是对象,这些狗对象便是狗类实例出来的

而对于这些具体的狗,她们通常都有名字和年龄(属性),都会打滚和撒泼(行为),就将这些公共的封装到类中

根据Dog类创建的每个实例都将存储名字和年龄。我们赋予了每条小狗蹲下(sit())和打滚(roll_over())的能力

#Python 3中title()方法:返回"标题化"的字符串,就是说所有单词的首个字母转化为大写,其余字母均为小写

#请注意,非字母后的第一个字母将转换为大写字母

#基本语法:str.title()

#!/usr/bin/python3

str = "this is string example from runoob....wow!!!"

print (str.title())

txt = "hello b2b2b2 and 3g3g3g"

x = txt.title()

print(x)

#输出结果

#This Is String Example From Runoob....Wow!!!

#Hello B2B2B2 And 3G3G3G

#Python3 istitle()方法:检测字符串中所有的单词拼写首字母是否为大写,且其他字母为小写。

#基本语法:str.istitle()

在类体或者函数体或者 .py 文档中,未赋给变量的第一个字符串通常是对类或函数或文档的整体文档功能的描述信息

class Dog():

#根据约定,在Python中,首字母大写的名称指的是类。这个类定义中的括号是空的,因为我们要从空白创建这个类。表示不继承既有类,

"""这是一次模拟小狗的简单使用"""

def __init__(self,name,age):

"""初始化小狗的属性名字和年龄"""

self.name = name

self.age = age

def sit(self):

"""模拟小狗被命令执行蹲下操作"""

print(self.name.title()+'is now sitting')

dog1 = Dog('哈士奇',5)

print(dog1)

dog1.sit()

1.2.1方法 __init__()

在类中定义的的函数我们称为方法;有关函数的一切都适用于方法,唯一重要的差别是调用方法的方式。而__init__()是一个特殊的方法,每当你根据Dog类创建新实例时,Python都会自动运行它(无需手动调用执行)(类似于JavaScript中class类的constructor构造函数)。

该方法最常见的用处:接受这些形参的值,并将它们存储在根据这个类创建的实例的属性中。

在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。所以我们自定义的标识符不推荐使用_作为开头,而是将其作为分隔符。

且 __init__()方法定义成了包含三个形参:self、name和age。只有self是固定的必须有的,还必须位于其他形参的前面,表示当前类的实例对象

创建Dog实例时,Python将调用Dog类的方法 __init__()。我们将通过实参向Dog()传递名字和年龄;self会自动传递,因此我们不需要传递它。每当我们根据Dog类创建实例时,都只需给最后两个形参(name和age)提供值。

为何必须在方法定义中包含形参self呢?

因为Python调用这个__init__()方法来创建Dog实例时,将自动传入实参self(和形参self进行区分)。每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法(个人理解:感觉这句不太对,应该是在类中能够访问实例上定义的属性和方法)。

而在 __init__()函数体(方法)中:定义的两个变量都有前缀self。以self为前缀的变量都可供类中的所有方法使用(因为这相当于为self就是实例对象添加属性,而类其他函数都自动传self这个参数),我们还可以通过类的任何实例来访问这些变量。self.name = name获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例。

像这样可通过实例访问的变量称为属性。访问的变量称为方法

类中定义的其它函数(作为实例的方法)通常只有一个形参self,除非你需要在调用实例的方法时需要传递参数。而创建的实例能够访问这些方法,相当于是所有实例的公共方法,

1.2.2根据类创建实例

可将类视为有关如何创建实例的说明。Dog类是一系列说明,让Python知道如何创建表示特定小狗的实例。而这些实例都携带类中定义的公共属性和方法。

python中是不支持字符串和数字进行相加的,不会像JavaScript一样隐式将数字转换成字符串进行相加,你需要使用str()方法将数字转化成字符串才能与其他字符串进行相加。

类的实例化时,dog1 = Dog('willie',6),Python使用实参'willie'和6调用Dog类中的方法__init__()。方法__init__()创建一个表示特定小狗的示例,并使用我们提供的值来设置属性name和age。注意:__init__()并未显式地包含return语句,但Python自动返回一个表示这条小狗的实例。我们将这个实例存储在变量dog1中。

命名约定很有用:我们通常可以认为首字母大写的名称(如Dog)指的是类,而小写的名称(如my_dog)指的是根据类创建的实例。

要访问实例的属性,可使用句点表示法。这种语法演示了Python如何获悉属性的值。句号表示通过这个对象中寻找某个属性或者方法

根据Dog类创建实例后,就可以使用句点表示法来调用Dog类中定义的任何方法。要调用方法,可指定实例的名称(这里是my_dog)和要调用的方法,并用句点分隔它们。遇到代码my_dog.sit()时,Python在类Dog中查找方法sit()并运行其代码。注意:如果不加括号,表示对该方法的定义的引用

在Dog类中引用这个属性时,使用的是self.name。在外部获取实例的属性时,使用的是dog1.name。

我们给另一个实例指定同样的名字和年龄,Python依然会根据Dog类创建另一个实例。你可按需求根据一个类创建任意数量的实例,条件是将每个实例都存储在不同的变量中,或占用列表或字典的不同位置。

谨记:类名首字母要大写,实例对象要小写!

1.2.3修改通过类实例的对象

你的大部分时间都将花在根据类创建的实例上。你需要执行的一个重要任务是修改实例的属性。你可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。

1.2.4给属性指定默认值和修改属性值

类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。在有些情况下,如设置默认值时,在方法__init__()内指定这种初始值是可行的;如果你对某个属性这样做了,就无需包含为它提供初始值的形参(这个形参就是可选的了)。

设置默认值的作用是,为后续操作修改属性值做准备!

#在__init__中指定属性值的默认值

class Dog():

def __init__(self,name,age):

self.odometer_reading = 0

修改属性值有三种方法

直接通过实例进行修改;

通过方法进行设置;

通过方法进行递增(增加特定的值)。

1. 直接修改属性的值(通过实例直接访问它并修改)

my_new_car.odometer_reading = 23

我们使用句点表示法来直接访问并设置汽车的属性odometer_reading。

这行代码让Python在实例my_new_car中找到属性odometer_reading,并将该属性的值设置为23

2. 通过方法修改属性的值(无需直接访问属性,而可将值传递给一个方法,由它在内部进行更新。)

#注意,这个方法是定义在汽车类中的!!

def update_odometer(self, mileage):

"""将里程表读数设置为指定的值"""

self.odometer_reading = mileage

该方法接受一个参数(就是想要修改成什么值),在方法内部获取实例的属性进行修改

主要我们调用这个方法并传入值就能达到修改的目的。

拓展:禁止任何人将里程表读数往回调

def update_odometer(self, mileage):

"""

将里程表读数设置为指定的值

禁止将里程表读数往回调

"""

if mileage >= self.odometer_reading:

self.odometer_reading = mileage

else:

print("You can't roll back an odometer!")

上述代码就要求里程数只能增加,不能往回调减小。

3. 通过方法对属性的值进行递增

有时候需要将属性值递增特定的量,而不是将其设置为全新的值。

#注意,这个方法是定义在汽车类中的!!

def increment_odometer(self, miles):

"""将里程表读数增加指定的量"""

self.odometer_reading += miles

扩展:以禁止增量为负值,从而防止有人利用它来回拨里程表。

注意!!你可以使用类似于上面的方法来控制用户修改属性值(如里程表读数)的方式,但能够访问程序的人都可以通过直接访问属性来将里程表修改为任何值。所以这还不能完全阻止用户回调数据。!!!

1.3类中的继承(重中之重)

编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。(现在第三方模块大部分都采用了集成)(大部分框架都会给我们封装好一些类,我们在写类可以继承这些既有类!)

1.3.1子类的方法__init__()

创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值。为此,子类的方

法__init__()需要父类施以援手.

class Car():

"""一次模拟汽车的简单尝试"""

def __init__(self, make, model, year):

self.make = make

self.model = model

self.year = year

self.odometer_reading = 0

def get_descriptive_name(self):

long_name = str(self.year) + ' ' + self.make + ' ' + self.model

return long_name.title()

def read_odometer(self):

print("This car has " + str(self.odometer_reading) + " miles on it.")

def update_odometer(self, mileage):

if mileage >= self.odometer_reading:

self.odometer_reading = mileage

else:

print("You can't roll back an odometer!")

def increment_odometer(self, miles):

self.odometer_reading += miles

class ElectricCar(Car):

"""电动汽车的独特之处"""

def __init__(self, make, model, year,size):

"""初始化父类的属性"""

super().__init__(make, model, year)

#定义子类特有属性

self.battery_size = size

#定义子类特有方法

def describe_battery(self):

"""打印一条描述电瓶容量的消息"""

print("This car has a " + str(self.battery_size) + "-kWh battery.")

my_tesla = ElectricCar('tesla', 'model s', 2016,70)

print(my_tesla.get_descriptive_name())

很明显,子类ElectricCar已经完全自动继承了父类定义的方法(除了父类的__init__方法没有自动继承),

我们定义类的__init__方法通常适用于设置公共属性的(所有实例都又有该属性但值可以不一样,这取决于参数),如果想让子类的__init__也继承父类的__init__,这就需要在子类的__init__方法体中写super().__init__(make, model, year),表示调用父类的__init__方法,同时父类的__init__方法的slef形参指代的是子类实例对象,其他继承的父类的函数的self形参也指代的是子类实例对象

创建子类时,父类必须包含在当前文件中,且位于子类前面。

定义子类时,必须在括号内指定父类的名称。方法__init__接受创建ElectricCar实例所需的信息

super()是一个特殊函数,帮助Python将父类和子类关联起来。

而super().__init__(make, model, year)表示调用父类的__init__()方法并将实参传进去

让ElectricCar实例包含父类的所有属性。父类也称为超类(superclass),名称super因此而得名。

默认已经继承父类的所有的方法(后续父类会有专属静态方法),而父类属性继承需要在子类__init__调用super().__init__()方法

我们还能定义电动汽车特有的属性和方法

1.3.2定义子类的特有属性和方法

让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。(具体代码在上边)

定义属性的初始值

在子类的__init__()方法中,除了可以继承父类的__init__()方法:super().__init__(make, model, year),还可以定义子类特有属性:self.battery_size = size,定义初始值/默认值的方法:self.battery_size = 70,这样就不用传这个参数了,内部相当于为这个属性定义了一个固定值。或者!!定义默认参数,就是在函数的参数那里比如:__init__(make, model, year=100),这也是定义参数默认值的一种方法。!这个形参是可选的:如果没有给它提供值,电瓶容量将被设置为70。

如果一个属性或方法是任何汽车都有的,而不是电动汽车特有的,就应将其加入到Car类而不是ElectricCar类中。这样,使用Car类的人将获得相应的功能,而ElectricCar类只包含处理电动汽车特有属性和行为的代码。

1.3.3重写父类的方法

对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。

可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。(类似于JS原型继承就近原则)

父类也有fill_gas_tank()这个方法,子类跟他重名就不会使用父类的这个方法

def ElectricCar(Car):

--snip--

def fill_gas_tank():

"""电动汽车没有油箱"""

print("This car doesn't need a gas tank!")

Python将忽略Car类中的方法fill_gas_tank(),转而运行上述代码。使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕。

1.3.4将实例用作属性

使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分成多个协同工作的小类。

不断给ElectricCar类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可将这些属性和方法提取出来,放到另一个名为Battery的类中,并将一个Battery实例用作ElectricCar类的一个属性。

这种大型类拆分成多个协同工作的小类是非常好用的,我们就可以将属于小类的属性和方法从大类中摘出来放到小类中,然后在大类的__init__()方法中为小类实例化出一个对象。

注意:小类的self指向的不再是大类实例对象,而是指向小类实例的对象,很可能该实例在大类的属性上。

这看似做了很多额外的工作,但现在我们想多详细地描述电瓶都可以,且不会导致ElectricCar类混乱不堪。

可以试着理解下:但是有的时候一个属性很明显是大类上的属性,而小类中定义的方法可能需要根据这个属性进行判断,我们的解决方法有两种,一种是将这个大类的属性定义在小类上形参上,而实参则由大类实例化时的参数提供,但这有点混乱,明明是给大类传递的参数,却导致不能直接在大类上打印出该属性,另一种方法是,该属性仍然定义在大类上,而小类想使用,就可以在小类的方法定义一个参数,在实例化时将大类的这个属性传进来。

小阅读:模拟较复杂的物件(如电动汽车)时,需要解决一些有趣的问题。续航里程是电瓶的属性还是汽车的属性呢?如果我们只需描述一辆汽车,那么将方法get_range()放在Battery类中也许是合适的;但如果要描述一家汽车制造商的整个产品线,也许应该将方法get_range()移到ElectricCar类中。在这种情况下,get_range()依然根据电瓶容量来确定续航里程,但报告的是一款汽车的续航里程。我们也可以这样做:将方法get_range()还留在Battery类中,但向它传递一个参数,如car_model;在这种情况下,方法get_range()将根据电瓶容量和汽车型号报告续航里程。

1.4导入类

上述我们虽然将大类分解成很多小类,但是当大类非常复杂时,整个文件也是比较大的难维护的,这是我们就可以将小类写在其他文件中,形成模块化开发,

随着你不断地给类添加功能,文件可能变得很长,即便你妥善地使用了继承亦如此。Python允许你将类存储在模块中,然后在主程序中导入所需的模块。

1.4.1导入单个类

模块化涉及一个很重要的问题:命名问题!!所以要求:使用模块的程序都必须使用更具体的文件名,

1.4.2一个模块中存储多个类

1.4.3从一个模块中导入多个类

1.4.4导入整个模块

1.4.5导入模块中所有类

1.4.6

1.5最后的总结

1.5.1 Python标准库

Python标准库是一组模块,安装的Python都包含它。这是内置在python环境中的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值