python 类

面向对象是最有效的软件编写方法之一,在面向对象编程中,你编写显示世界中的事物和情景类,并基于这些类来创建对象,编写类时,你定义类对象都有通用的行为,基于类创建对象时,每个对象都自动具备这些通用行为,然后可根据需要赋予每个对象独特的个性,使用面向对象来模拟实现情景.

更具类创建对象被称为实例化,这能够让你使用类的实例,学习中,你将编写一些类并创建其实例,你将指定可在实例中存储什么信息,定义可对这些实例执行那些操作,你还将编写一些类来扩展既有类的功能,让相似的类能够高效地共享代码,你把自己编写的类存储在模块中,并在自己的程序文件中带入其他程序编写的类.

1.创建和使用类
根据dog类创建的每个实例都将存储名字和年龄,我们赋予了每条小狗跑和吃的能力.
在这里插入图片描述
我们定义了一个Dog类,在python中首字母大写的名称值的是类,这个类定义中的括号是空的,我们要从空白处创建这个类.

2.方法__init__()
类中的函数称为方法,init()是一个特别的方法,每当你根据Dog类创建新实例时,python都会自动运行它,在这
个方法名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免python默认方法与普通方法发生冲突.

我们将方法__init__()定义成了包含三个形参:self、name、age.在这个方法的定义中,形参self必不可少,还必须位于其他形参前面,为何必须在方法定义中包含形参self呢?因为python调用这个__init__()方法来创建Dog实例时,将自动传入实参self,每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法,我们创建Dog实例时,python都将调用Dog类的方法,我们将通过实参Dog()传递名字和年龄,self会自动递增,因此我们不需要传递它,我们根据Dogl类创建实例时,都只需给最后形参(name和age)提供值.

在__init__中,两个变量前面都有前缀self,以self为前缀的变量都可供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量,self.name=name获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例,self.age =age的作用与此类类似,通过实例访问的变量称为属性.

Dog中还定义了另外两个方法:run()和eat(),由于这些方法可供类中所有方法使用,由于这些方法不需要额外的信息,如名字或年龄,因此他们只有一个形参self,我们后面将创建的实例能够访问这些方法,就是,他们都能够跑和吃,run()和eat()所做的都是有限的,他们只打印一条消息,指出小狗正跑和吃,但可以扩充这些方法模拟实际情况.

3.根据类创建实例
可将类视为有关如何创建实例的说明.Dog类是一系列的说明,让python知道如何创建表示特定小狗的实例.

下面创建创建一个表示特定小狗的实例.
在这里插入图片描述
我们让python创建一条名为’jiyi’、年龄为6的小狗,遇到这行代码,python使用实参’jiyi’和调用Dog类中的方法__init__().方法__init__()创建一个表示特定小狗的实例,并使用我们提供的值来设置属性name和age.方法__init__()并为显示地包含return语句,但是python自动返回一个表示小狗的实例,我们将这个实例存储在变量my_dog中,在这里,命令约定很有用,我们通常可以认为首字母大写的名称(如Dog)指的是类,而小写的名称(如my_dog)指的是类创建的实例.

4.访问属性
要访问实例的属性,可使用句点表示法,我们编写了如下代码访问my_dog的属性name的值.

my_dog.name

句点表示法在python中很常用,通过语法演示了python如何获悉属性的值,在这里,python先找到my_dog,再查找与这个实例相关联的属性name.在Dog类中引用这个属性时,使用self.name,我们使用同样的方法来获取属性age的值,在前面的第一条print语句中,my_dog.name.title()将my_dog的属性name的值’jiyi’改为首字母大写的,在第二条print语句中,str(my_dog.age)将my_dog的属性age的值6转换为字符串.
在这里插入图片描述

5.调用方法
根据Dog类创建实例后,就可以使用句点表示法来调用Dog类中定义的任何方法,下面让小狗跑和吃.
在这里插入图片描述
要调用方法,可以指定实例的名称(这里是my_dog)和要调用的方法,并用句点分隔他们,遇到代码my_dog.run()时,python在类Dog中查找方法run()并运行其代码,python以同样的方式解读代码my_dog.eat().
在这里插入图片描述
这种语法很有用,如果给属性和方法指定了合适的描述名称,如name、age、eat和run,即便是从未见过的代码块,我们也能够轻松推断出他是做什么的.

5.创建多个实例
可按需求根据类创建任意数量的实例,下面创建一个名为your_dog的实例.
在这里插入图片描述
在这个实例中,我们创建了两条小狗,他们分别为jiyi和luosang,每条小狗都是一个独立的实例,有自己的一组属性,能够执行相同的操作.
在这里插入图片描述
就算我们给第二条小狗指定相同的名字和年龄,python依然会根据Dog类创建另一个实例,你可按需求根据一个类创建任意数量的实例,条件是将每个实例都存储在不同的变量中,或占用列表或字典的不同位置.

6.使用类和实例
你可以使用类来模拟现实生活中很多情景,类编写好后,你的大部分时间都将花在使用根据类创建的实例上,你需要执行的一个重要任务是修改实例的属性,你可以直接修改实例的属性,也可以编写方法以特定的方式进行修改.

7.Car类
下面编写一个表示汽车的类,他存储了有关汽车的信息,还有一个汇总这些信息的方法.
在这里插入图片描述
我们定义了方法__init__(),与前面的Dog类中一样,这个方法的第一个形参为self;我们还在这个方法中包含了另外三个形参:make、model和year.方法__init__()接受这些形参的值,并将他们存储在根据这个类创建的实例的属性中,创建新的Car实例时,我们需要指定其制造商、星号和生产年份.

我们定义了名为get_descriptive_name()的方法,它使用year、make和model创建一个对汽车进行描述的字符串,让我们分别打印每个属性的值,为在这个方法中访问属性的值,我们使用self.make、self.model和self.year.我们根据Car类创建了一个实例,并将其存储到变量my_new_car中,接下来,我们调用方法get_descriptive_name(),指出我们拥有的什么样的汽车.

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

下面我们来添加一个名为odometer_reading的属性,其初始值总是为0,我们还添加一个名为read_odometer()的方法,用于读取汽车的里程表:
在这里插入图片描述
现在,当python调用方法__init__()来创建实例时,将像前一个实例一样属性的方式存储制造商、型号和生产年份.接下来,python将创建一个名为odometer_reading的属性,其将初始值设置为0,我们还定义了一个read_odometer()方法,它让你能够轻松获悉汽车的里程.

一开始汽车的里程为0:
在这里插入图片描述
出售时里程表读数为0的汽车并不多,因此我们需要一个修改该属性的值的途径.

9.修改属性的值
可以以三种不同的方式修改属性的值:直接通过实例进行修改,通过方法进行设置;通过方法进行递增(增加特定的值),依次介绍这些方法.
在这里插入图片描述
我们使用句点表示法来直接访问并设置汽车的属性odometer_reading.这行代码让python在实例my_new_car中找到属性odometer_reading,并将该属性的值设置为23:
在这里插入图片描述
有时候需要像这样直接访问属性,但其他时候需要编写对属性进行更新的方法.

10.通过方法修改属性的值
如果有人替你更新属性的方法,这样就无需直接访问属性,而可将值传递给一个方法,它在内部进行更新.

下面的示列演示一个update_odometer()方法:
在这里插入图片描述
对Car类所做的唯一修改是在添加了方法upadte_odometer().这个方法接受一个里程值,并将其存储到self.odometer_reading中,后我们调用update_odometer(),并向它提供实参(对应方法定义中的形参mileage).它将里程表读数设置为23;而方法read_odometer()打印该函数.
在这里插入图片描述
可对方法update_odometer()进行扩展,使其在修改里程表读数时做些额外的工作,下面来添加一些逻辑,禁止任何人将里程表读数往回调:
在这里插入图片描述
现在,update_odometer()在修改属性前检查指定的读数是否合理,如果新指定的里程(mileage)大于或等于原来的里程(self.odometer_reading),就将里程表读数改为指定的里程;否则就发出警告,指出不能将里程表往回拨.

11.通过方法对属性的值进行递增.
有时候需要将属性值递增特定的量,而不是将其设置为全新的值,假设我们有一辆二手车,且购买到登记期间增加了100英里的里程,下面的方法让我们能够传递这个增量,并相应地增加里程表读数:
在这里插入图片描述
增加新的方法increment_odometer()接受一个单位为英里的数字,并将其加入到self.odometer_reading中.我们创建了一辆二手车–my_userd_car.我们调用方法update_odometer()并传入23000,将这辆二手车的里程表读数设置为23000,我们调用increment_odometer()并传入100,以增加从购买到登记期间行使的100英里.
在这里插入图片描述
注意:你可以使用类似于上面的方法来控制用户修改属性值(如果里程表读数)的方式,但是能够访问程序的人都可以通过访问属性来将里程表修改为任何值,要确保安全,除了进行类似于前面的基本检查外,还需要特别注意细节.

12.继承
编写父类时,并非总是要从空白处开始,如果你要编写的类是另一个现成类的特殊版,可以使用继承,一个类继承另一个类时,他将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类,子类继承了其父类的所有属性
和方法,同时还可以定义自己的属性和方法.

12.1 子类的方法__init__()
创建子类的实例时,python首先需要完成的任务是给父类的所有属性赋值,为此,子类的方法__init__()需要父类施以援手

我们来模拟电动车,电动车是一种特殊的汽车,因此我们可以在前面创建的Car类的基础上创建新类ElectricCar,这样我们就只需要为电动车特有的属性和行为编写代码.

我们创建一个简单的ElectricCar类版本,它具备Car类的所有功能:

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 upadte_odometer(self,mileage):
        '''将里程表读数设置为指定的值
           禁止将里程表读数往回调
        '''
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You cant's roll back an odometer!")

    def increment_odometer(self,miles):
        '''将里程表读数增加指定的量'''
        if miles >= 0:
            self.odometer_reading += miles
        else:
            print("It cant's be negative")
class ElectricCar(Car):
    '''电动车的独特之处'''
    def __init__(self,make,model,year):
        '''初始化父类的属性'''
        super().__init__(make,model,year)

my_tesla = ElectricCar('tesla','model"s',2016)
print(my_tesla.get_descriptive_name())

首先是Car类的代码,创建子类时,父类必须包含在当前文件中,且谓语子类面前,我们定义了子类ElectricCar.定义子类时,必须在括号内指定父类的名称,方法__init__()接受创建Car实例所需要的信息.

super()也是一个特殊函数,帮助python将父类和子类关联起来,这行代码让python调用ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类的所有属性,父类也称为超类,名称super因此而得名.

除方法__init__()外,电动车没有其他特有的属性和方法,当前,我们只想确认电动汽车具备普通汽车的行为:
在这里插入图片描述
12.2 给子类定义属性和方法
让一个类继承另一个类,可添加区分类和父类所需的新属性和方法
下面来添加一个电动车特有的属性,以及描述该属性的方法,我们将存储电瓶容量,并编写一个打印电瓶描述的方法:
在这里插入图片描述
我们添加了新属性self.battery_size,并设置其初始值(70),根据ElectricCar类创建的所有实例都将包含在这个属性;但所有Car实例都不包含它,我们还添加了一个名为describle_battery()方法,它打印有关电瓶的信息,我们调用这个方法时,将看到一条电动车特有的描述:
在这里插入图片描述
对于ElectricCar类的特殊化程度没有任何限制,模拟电动汽车时,你可以更具需要的准确程度添加任意属性和方法,如果一个属性或方法是任何汽车都有的,而不是电动汽车特有的,就应该加入到Car类而不是ElectriCar类中,这样,使用Car类的人将获得相应的功能,而ElectricCar类只包含处理电动汽车特有属性和行为代码.

12.3 重写父类的方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写,为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名,这样,python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法.

假设Car类有一个名为fill_gas_tank()的方法,它对电动车来说毫无意义,因此你可能想重写,下面演示重写方法:
现在,如果有人要调用对电动车fill_gas_tank(),python将忽略Car类中的方法fill_gas_tank(),转而运行上面代码,使用继承时,可让子类保留从父类哪里继承而来的精华,剔除糟粕.
在这里插入图片描述

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

我们不断给ElectricCar类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法,在这种情况下,我们可以将这些属性和方法提取出来,放到另一个文件为Battery的类中,并将一个Battery实例用做ElectricCar类的一个属性:
在这里插入图片描述
我们定义了一个名为Battery的新类,它没有继承任何类,init()除self外,还有另外一个形参battery_size.这个形参是可选的,如果没有给他提供值,电瓶容量将被设置为70,方法describle_battwey()也移到了这个类中.

在ElectricCar类中,我们添加了一个名为self.battery的属性,这行代码让python创建一个新的Battery实例,并将该实例存储在属性self.battery中,每当方法__init__()被调用时,都将执行该操作,因此每个ElectricCar实例都包含一个自动创建的Battery实例.
在这里插入图片描述
我们想要详细地描述电瓶都可以,且不会导致ElectricCar混乱不堪,下面在给Battery类添加一个方法,它根据电瓶容量报告汽车的续航里程:
在这里插入图片描述
在这里插入图片描述
新增的方法get_range()做了一些简单的分析,如果电瓶容量为70KWh,他就将续航里程设置为240英里;如果容量为85Kwh,就将续航里程设置为270英里,然后报告这个值,为了使用这个方法,我们也通过汽车的属性battery来调用它.

输出指出了汽车的续航里程(取决于电瓶的容量)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值