Python之类

面向对象编程 是最有效的软件编写方法之一。 在面向对象编程中, 你编写表示现实世界中的事物和情景的类, 并基于这些类来创建对象。 编写类时, 你定义一大类对象都有的通用行为。 基于类创建对象 时, 每个对象都自动具备这种通用行为, 然后可根据需要赋予每个对象独特的个性。 使用面向对象编程可模拟现实情景, 其逼真程度达到了令你惊讶的地步。

1.1.1创建Dog 类

class Dog():
"""一次模拟小狗的简单尝试"""
def __init__(self, name, age):
"""初始化属性name和age"""
self.name = name
self.age = age
def sit(self):
"""模拟小狗被命令时蹲下"""
print(self.name.title() + " is now sitting.")
def roll_over(self):
"""模拟小狗被命令时打滚"""
print(self.name.title() + " rolled over!")

方法__init__()
类中的函数称为方法,唯一的差别是调用方式不同。方法__init__() 是一个特殊的方法, 每当你根据Dog 类创建新实例时, Python都会自动运行它。 在这个方法的名称中, 开头和末尾各有两个下划线, 这是一种约定, 旨在避免Python默认方法与普通方法发生名称冲突。
我们将方法__init__() 定义成了包含三个形参: self 、 name 和age 。 在这个方法的定义中, 形参self 必不可少, 还必须位于其他形参的前面。 为何必须在方法定义中包含形参self 呢? 因为Python调用这个__init__() 方法来创建Dog 实例时, 将自动传入实参self 。 每个与类相关联的方法调用都自动传递实参self , 它是一个指向实例本身的引用, 让实例能够访问类中的属性和方法。 我们创建Dog 实例时, Python将调用Dog 类的方法__init__() 。 我们将通过实参向Dog() 传递名字和年龄; self 会自动传递,因此我们不需要传递它。 每当我们根据Dog 类创建实例时, 都只需给最后两个形参(name 和age ) 提供值。

1.1.2根据类创建实例

my_dog = Dog('willie', 6)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")


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

1.1.3调用方法和调用属性

my_dog.name
my_dog.age

my_dog = Dog('willie', 6)
my_dog.sit()
my_dog.roll_over()

也可创建多个实例

1.2.1给属性指定默认值


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.")
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

1.2.2 修改属性的值

直接修改:

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.")
my_new_car = Car('audi', 'a4', 2016)
my_new_car.odometer_reading=23
print(my_new_car.get_descriptive_name())

通过方法修改属性值

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_reading(self,odometer_reading):
        self.odometer_reading=odometer_reading

my_new_car = Car('audi', 'a4', 2016)
my_new_car.update_odometer_reading(23)
print(my_new_car.get_descriptive_name())
print(my_new_car.odometer_reading)
my_new_car.read_odometer()

通过方法设置递增

class Car():
    def __init__(self, make, model, year):
        """初始化描述汽车的属性"""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 2
    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_reading(self,miles):
        self.odometer_reading += miles

my_new_car = Car('audi', 'a4', 2016)
my_new_car.update_odometer_reading(23)
print(my_new_car.get_descriptive_name())
print(my_new_car.odometer_reading)
my_new_car.read_odometer()

继承

编写类时, 并非总是要从空白开始。 如果你要编写的类是另一个现成类的特殊版本, 可使用继承 。 一个类继承 另一个类时, 它将自动获得另一个类的所有属性和方法; 原有的类称为父类 , 而新类称为子类 。 子类继承了其父类的所有属性和方法, 同时还可以定义自己的属性和方法。
创建子类的实例时,首先要对父类的所有属性赋值,为此,子类的方法__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):
"""初始化父类的属性"""
❹ super().__init__(make, model, year)
❺ my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())

创建子类时,父类必须包含在当前文件中,且位于子类前面。在❷处, 我们定义了子类ElectricCar 。 定义子类时, 必须在括号内指定父类的名称。 方法__init__() 接受创建Car 实例所需的信息(见❸) 。❹处的super() 是一个特殊函数, 帮助Python将父类和子类关联起来。 这行代码让Python调用ElectricCar 的父类的方法__init__() , 让ElectricCar 实例包含父类的所有属性。 父类也称为超类 (superclass) , 名称super因此而得名。
为测试继承是否能够正确地发挥作用, 我们尝试创建一辆电动汽车, 但提供的信息与创建普通汽车时相同。 在❺处, 我们创建ElectricCar 类的一个实例, 并将其存储在变量my_tesla 中。 这行代码调用ElectricCar 类中定义的方法__init__() , 后者让Python调用父类Car 中定义的方法__init__() 。 我们提供了实参'tesla' 、 'models' 和2016 。除方法__init__() 外, 电动汽车没有其他特有的属性和方法。 

1.3.1给子类定义属性和方法(没有运行出来,有运行出来的麻烦告我这是否对)

class Car():
--snip--
class ElectricCar(Car):
"""Represent aspects of a car, specific to electric vehicles."""
def __init__(self, make, model, year):
"""
电动汽车的独特之处
初始化父类的属性, 再初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
❶ self.battery_size = 70
❷ def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()

1.3.2 重写父类的方法

对于父类的方法, 只要它不符合子类模拟的实物的行为, 都可对其进行重写。 为此, 可在子类中定义一个这样的方法, 即它与要重写的父类方法同名。 这样, Python将不会考虑这个父类方法, 而只关注你在子类中定义的相应方法。
假设Car 类有一个名为fill_gas_tank() 的方法, 它对全电动汽车来说毫无意义, 因此你可能想重写它。 下面演示了一种重写方式:
def ElectricCar(Car):
     --snip--
def fill_gas_tank():
   """电动汽车没有油箱"""
    print("This car doesn't need a gas tank!")

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

1.3.3 将实例用作属性
使用代码模拟实物时, 你可能会发现自己给类添加的细节越来越多: 属性和方法清单以及文件都越来越长。 在这种情况下, 可能需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分成多个协同工作的小类。
例如, 不断给ElectricCar 类添加细节时, 我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。 在这种情况下, 我们可将这些属性和方法提取出来, 放到另一个名为Battery 的类中, 并将一个Battery 实例用作ElectricCar 类的一个属性。

1.3.4 模拟实物

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

这让你进入了程序员的另一个境界: 解决上述问题时, 你从较高的逻辑层面(而不是语法层面) 考虑; 你考虑的不是Python, 而是如何使用代码来表示实物。 到达这种境界后, 你经常会发现, 现实世界的建模方法并没有对错之分。 有些方法的效率更高, 但要找出效率最高的表示法, 需要经过一定的实践。 只要代码像你希望的那样运行, 就说明你做得很好! 即便你发现自己不得不多次尝试使用不同的方法来重写类, 也不必气馁; 要编写出高效、 准确的代码, 都得经过这样的过程。

1.3.5 导入类

 

小试牛刀,检验一下自己吧!

9-1 餐馆 : 创建一个名为Restaurant 的类, 其方法__init__() 设置两个属性: restaurant_name 和cuisine_type 。 创建一个名
为describe_restaurant() 的方法和一个名为open_restaurant() 的方法, 其中前者打印前述两项信息, 而后者打印一条消息, 指出餐馆正在营业。
根据这个类创建一个名为restaurant 的实例, 分别打印其两个属性, 再调用前述两个方法。
9-2 三家餐馆 : 根据你为完成练习9-1而编写的类创建三个实例, 并对每个实例调用方法describe_restaurant() 。
9-3 用户 : 创建一个名为User 的类, 其中包含属性first_name 和last_name , 还有用户简介通常会存储的其他几个属性。 在类User 中定义一个名为describe_user() 的方法, 它打印用户信息摘要; 再定义一个名为greet_user() 的方法, 它向用户发出个性化的问候。
创建多个表示不同用户的实例, 并对每个实例都调用上述两个方法。
9-4 就餐人数 : 在为完成练习9-1而编写的程序中, 添加一个名为number_served 的属性, 并将其默认值设置为0。 根据这个类创建一个名为restaurant 的实
例; 打印有多少人在这家餐馆就餐过, 然后修改这个值并再次打印它。
添加一个名为set_number_served() 的方法, 它让你能够设置就餐人数。 调用这个方法并向它传递一个值, 然后再次打印这个值。
添加一个名为increment_number_served() 的方法, 它让你能够将就餐人数递增。 调用这个方法并向它传递一个这样的值: 你认为这家餐馆每天可能接待的就餐人数。
9-5 尝试登录次数 : 在为完成练习9-3而编写的User 类中, 添加一个名为login_attempts 的属性。 编写一个名为increment_login_attempts() 的方法,它将属性login_attempts 的值加1。 再编写一个名为reset_login_attempts() 的方法, 它将属性login_attempts 的值重置为0。
根据User 类创建一个实例, 再调用方法increment_login_attempts() 多次。 打印属性login_attempts 的值, 确认它被正确地递增; 然后, 调用方法reset_login_attempts() , 并再次打印属性login_attempts 的值, 确认它被重置为0。
9-6 冰淇淋小店 : 冰淇淋小店是一种特殊的餐馆。 编写一个名为IceCreamStand 的类, 让它继承你为完成练习9-1或练习9-4而编写的Restaurant 类。 这两个版
本的Restaurant 类都可以, 挑选你更喜欢的那个即可。 添加一个名为flavors 的属性, 用于存储一个由各种口味的冰淇淋组成的列表。 编写一个显示这些冰淇淋
的方法。 创建一个IceCreamStand 实例, 并调用这个方法。
9-7 管理员 : 管理员是一种特殊的用户。 编写一个名为Admin 的类, 让它继承你为完成练习9-3或练习9-5而编写的User 类。 添加一个名为privileges 的属性, 用于存储一个由字符串(如"can add post" 、 "can delete post" 、 "can ban user" 等) 组成的列表。 编写一个名为show_privileges() 的方法, 它显示管理员的权限。 创建一个Admin 实例, 并调用这个方法。
9-8 权限 : 编写一个名为Privileges 的类, 它只有一个属性——privileges , 其中存储了练习9-7 所说的字符串列表。 将方法show_privileges() 移到这个类中。 在Admin 类中, 将一个Privileges 实例用作其属性。 创建一个Admin 实例, 并使用方法show_privileges() 来显示其权限。
9-9 电瓶升级 : 在本节最后一个electric_car.py版本中, 给Battery 类添加一个名为upgrade_battery() 的方法。 这个方法检查电瓶容量, 如果它不是85, 就将它设置为85。 创建一辆电瓶容量为默认值的电动汽车, 调用方法get_range() , 然后对电瓶进行升级, 并再次调用get_range() 。 你会看到这辆汽车的续航里程增加了
9-10 导入Restaurant 类 : 将最新的Restaurant 类存储在一个模块中。 在另一个文件中, 导入Restaurant 类, 创建一个Restaurant 实例, 并调用Restaurant 的一个方法, 以确认import 语句正确无误。
9-11 导入Admin 类 : 以为完成练习9-8而做的工作为基础, 将User 、 Privileges 和Admin 类存储在一个模块中, 再创建一个文件, 在其中创建一个Admin 实例并对其调用方法show_privileges() , 以确认一切都能正确地运行。
9-12 多个模块 : 将User 类存储在一个模块中, 并将Privileges 和Admin 类存储在另一个模块中。 再创建一个文件, 在其中创建一个Admin 实例, 并对其调用方法show_privileges() , 以确认一切都依然能够正确地运行。
 

 

 

 

 

 

 

 

 

 

 



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值