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!")
在❶处,我们定义了一个名为Dog 的类。根据约定,在Python中,首字母大写的名称指的是类。这个类定义中的括号是空的,因为我们要从空白创建这个类。
在❷处,我们编写了一个文档字符串,对这个类的功能作了描述。

1. 方法__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 )提供值。
❹处定义的两个变量都有前缀self 。以self 为前缀的变量都可供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量。self.name = name 获取存储在形
参name 中的值,并将其存储到变量name 中,然后该变量被关联到当前创建的实例。self.age = age 的作用与此类似。像这样可通过实例访问的变量称为属性
 
在Python 2.7中创建类时,需要做细微的修改——在括号内包含单词object:
class ClassName(object): 
  --snip--

1.2、根据类创建实例

class Dog(): 
  --snip--

❶ 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.")

1. 访问属性

要访问实例的属性,可使用句点表示法。

2. 调用方法

根据Dog 类创建实例后,就可以使用句点表示法来调用Dog 类中定义的任何方法。

2、使用类和实例

2.1、Car类

class Car(): 
  """一次模拟汽车的简单尝试"""
def __init__(self, make, model, year):
      """初始化描述汽车的属性"""
      self.make = make self.model = model self.year = year

def get_descriptive_name(self):
    """返回整洁的描述性信息"""
    long_name = str(self.year) + ' ' + self.make + ' ' + self.model
    return long_name.title()

❸ my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())

2.2、给属性指定默认值

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

2.3、修改属性的值

可以以三种不同的方式修改属性的值:直接通过实例进行修改;通过方法进行设置;通过方法进行递增(增加特定的值)。

3、继承

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 ElectriCar(Car):
  """电动汽车的独特之处"""

❸  def __init__(self, make, model, year):
    """初始化父类的属性"""
❹    super().__init__(make, model, year)

❺ my_tesla = ElectriCar('tesla','model s', 2016)
print(my_tesla.get_descriptive_name())
首先是Car 类的代码(见❶)。创建子类时,父类必须包含在当前文件中,且位于子类前面。在❷处,我们定义了子类ElectricCar 。定义子类时,必须在括号内指定父类的
名称。方法__init__() 接受创建Car 实例所需的信息(见❸)。
❹处的super() 是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用ElectricCar 的父类的方法__init__() ,让ElectricCar 实例包含父类的所
有属性。父类也称为超超类类 (superclass),名称super因此而得名。

3.2、Python2.7中的继承

在Python 2.7中,继承语法稍有不同,ElectricCar 类的定义类似于下面这样:
class Car(object): 

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

class ElectricCar(Car):

  def __init__(self, make, model, year):
    super(ElectricCar, self).__init__(make, model, year)
    --snip--
函数super() 需要两个实参:子类名和对象self 。为帮助Python将父类和子类关联起来,这些实参必不可少。
另外,在Python 2.7中使用继承时,务必在定义父类时在括号内指定object 。

3.3、给子类定义属性和方法

3.4、重写父类的方法

对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这
个父类方法,而只关注你在子类中定义的相应方法。
使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕。

3.5、将实例用作属性

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

3.6、模拟实物

4、导入类

4.1、导入单个类

4.2、在一个模块中存储多个类

4.3、在一个模块中导入多个类

"""一个可用于表示汽车的类"""

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 Battery(): 
    """一次模拟电动汽车电瓶的简单尝试""" 
    def __init__(self, battery_size=60): 
        """初始化电瓶的属性""" 
        self.battery_size = battery_size 

    def describe_battery(self): 
        """打印一条描述电瓶容量的消息""" 
        print("This car has a " + str(self.battery_size) + "-kWh battery.") 

    def get_range(self): 
        """打印一条描述电瓶续航里程的消息""" 
        range = 0
        if self.battery_size == 70: 
            range = 240 
        elif self.battery_size == 85: 
            range = 270 
        else:
            range = 210

        message = "This car can go approximately " + str(range) 
        message += " miles on a full charge."
        print(message)

class ElectricCar(Car):
    """电动汽车的独特之处"""

    def __init__(self, make, model, year):
        """初始化父类的属性"""
        super().__init__(make, model, year)
        self.battery = Battery()
from car import Car,ElectricCar

my_car = Car('audi', 'a4', 2016)
print(my_car.get_descriptive_name())

my_tesla = ElectricCar('tesla', 'model s', 2016) 
print(my_tesla.get_descriptive_name()) 
my_tesla.battery.describe_battery() 
my_tesla.battery.get_range()
从一个模块中导入多个类时,用逗号分隔了各个类。导入必要的类后,就可根据需要创建每个类的任意数量的实例。

4.4、导入整个模块

你还可以导入整个模块,再使用句点表示法访问需要的类。这种导入方法很简单,代码也易于阅读。由于创建类实例的代码都包含模块名,因此不会与当前文件使用的任何名称
发生冲突。

4.5、导入模块中的所有类

from module_name import *

4.6、在一个模块中导入另一个模块

4.7、自定义工作流程

正如你看到的,在组织大型项目的代码方面,Python提供了很多选项。熟悉所有这些选项很重要,这样你才能确定哪种项目组织方式是最佳的,并能理解别人开发的项目。
一开始应让代码结构尽可能简单。先尽可能在一个文件中完成所有的工作,确定一切都能正确运行后,再将类移到独立的模块中。如果你喜欢模块和文件的交互方式,可在项目
开始时就尝试将类存储到模块中。先找出让你能够编写出可行代码的方式,再尝试让代码更为组织有序。

5、Python标准库

from collections import OrderedDict

favorite_languages = OrderedDict()

favorite_languages['sarah'] = 'c' 
favorite_languages['edward'] = 'ruby' 
favorite_languages['phil'] = 'python'

for name,language in favorite_languages.items():
    print(name.title() + "'s favorite language is " 
        + language.title() + ".")
这是一个很不错的类,它兼具列表和字典的主要优点(在将信息关联起来的同时保留原来的顺序)。等你开始对关心的现实情形建模时,可能会发现有序字典正好能够满足需
求。随着你对标准库的了解越来越深入,将熟悉大量可帮助你处理常见情形的模块。

6、类编码风格

  • 类名应采用驼峰命名法 ,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。
  • 对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。
每个模块也都应包含一个文档字符串,对其中的类可用于做什么进行描述。
  • 可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。
  • 需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的import 语句,再添加一个空行,然后编写导入你自己编写的模块的import 语句。
在包含多条import 语句的程序中,这种做法让人更容易明白程序使用的各个模块都来自何方。