入门必备!面向对象编程之Python函数与类

来源:数据STUDIO

作者:云朵君

Python支持大多数面向对象编程技术。在Python中所有东西都是对象,包括类、函数、数和模块。它允许多态性,不只是在类层级之内而且通过采用鸭子类型的方式。任何对象可以用于任何类型,只要它有适当的方法和特性就能工作。

Python的函数支持递归和闭包及其他头等函数特征,但不支持函数重载。Python的函数作为第一类对象,具有和普通对象平等的地位。

本文继续总结Python基础中的重要部分--函数与类。

一、函数

函数用于完成一项特定的工作。函数允许您只编写一次代码,然后在需要完成相同任务时运行这些代码。函数可以接收它们需要的信息,并返回它们生成的信息。有效地使用函数可以使程序更容易编写、读取、测试和修复。

1、函数定义

函数的第一行是它的定义,用关键字def标记。函数名后面跟着一组圆括号和冒号。用三引号括起来的文档字符串描述函数的功能。函数体缩进一层。要调用一个函数,请在函数名之后加上一组圆括号。

我们可以自己定义一个函数,但是需要遵循以下的规则:

  • 函数的代码块以def关键字开头,后接函数名称和圆括号()。
  • 圆括号用来存储要传入的参数和变量,这个参数可以是默认的也可以是自定义的。
  • 函数内容以冒号起始,并且有强制缩进。
  • return 结束函数。选择性的返回一个值给对方调用。不带表达式的return相当于返回 None。
def greet_user():    '''Display a simple greeting.'''    print('Hello!')greet_user()

2、传入一个参数

def greet_user(username):    '''Display a personalized greeting.'''    print('Hello, ' + username + '!')greet_user('jesse')greet_user('diana')greet_user('brandon')

3、位置参数和关键参数

两种主要的参数是位置参数和关键字参数。

使用位置参数时,Python将函数调用中的第一个参数与函数定义中的第一个形参进行匹配,依此类推。

使用关键字参数,您可以指定每个参数应该在函数调用中赋值给哪个形参。当您使用关键字参数时,参数的顺序并不重要。

  • 位置参数
def describe_pet(animal, name):    '''Display information about a pet.'''    print('\nI have a ' + animal + '.')    print('Its name is ' + name + '.')    describe_pet('hamster', 'harry')describe_pet('dog', 'willie')
  • 关键参数
def describe_pet(animal, name):    '''Display information about a pet.'''    print('\nI have a ' + animal + '.')    print('Its name is ' + name + '.')describe_pet(animal='hamster', name='harry')describe_pet(name='willie', animal='dog')

4、默认参数

可以为参数提供默认值。当函数调用忽略此实参时,将使用默认值。在函数定义中,带有默认值的形参必须列在没有默认值的形参之后,这样位置实参仍然可以正常起作用。

  • 使用默认值
def describe_pet(name, animal='dog'):    '''Display information about a pet.'''    print('\nI have a ' + animal + '.')    print('Its name is ' + name + '.')describe_pet('harry', 'hamster')describe_pet('willie')
  • 使用None语使参数可选
def describe_pet(animal, name=None):    '''Display information about a pet.'''    print('\nI have a ' + animal + '.')    if name:        print('Its name is ' + name + '.')describe_pet('hamster', 'harry')describe_pet('snake')

5、返回值

函数可以返回一个值或一组值。当函数返回值时,调用行必须提供一个用于存储返回值的变量。函数在到达return语句时停止运行。

  • 返回单个值
def get_full_name(first, last):    '''Return a neatly formatted full name.'''    full_name = first + ' ' + last    return full_name.title()musician = get_full_name('jimi', 'hendrix')print(musician)
  • 返回一个字典
def build_person(first, last):    '''Return a dictionary of information    about a person.    '''    person = {'first': first, 'last': last}    return personmusician = build_person('jimi', 'hendrix')print(musician)
  • 返回一个包含可选值的字典
def build_person(first, last, age=None):    '''Return a dictionary of information    about a person.    '''    person = {'first': first, 'last': last}    if age:        person['age'] = age    return personmusician = build_person('jimi', 'hendrix', 27)print(musician)musician = build_person('janis', 'joplin')print(musician)

6、将列表传递给函数

您可以将列表作为参数传递给函数,函数可以使用列表中的值。该函数对列表所做的任何更改都将影响原始列表。你可以通过传递列表的副本作为参数来阻止函数修改列表。

  • 将列表作为参数传递
def greet_users(names):    '''Print a simple greeting to everyone.'''    for name in names:        msg = 'Hello, ' + name + '!'        print(msg)usernames = ['hannah', 'ty', 'margot']greet_users(usernames)
  • 允许函数修改列表

下面的示例将模型列表发送到一个函数以进行打印。原始列表被清空,第二个列表被填充。

def print_models(unprinted, printed):    '''3d print a set of models.'''    while unprinted:        current_model = unprinted.pop()        print('Printing ' + current_model)        printed.append(current_model)        # 存储一些未打印的设计,并打印每一个。unprinted = ['phone case', 'pendant', 'ring']printed = []print_models(unprinted, printed)print('\nUnprinted:', unprinted)print('Printed:', printed)
  • 阻止函数修改列表

下面的示例与前面的示例相同,只是在调用print_models()之后原始列表没有改变。

def print_models(unprinted, printed):    '''3d print a set of models.'''    while unprinted:        current_model = unprinted.pop()        print('Printing ' + current_model)        printed.append(current_model)# 存储一些未打印的设计,并打印每一个。original = ['phone case', 'pendant', 'ring']printed = []print_models(original[:], printed)  print('\nOriginal:', original)print('Printed:', printed)

7、传入任意数量的参数

有时你不知道一个函数需要接受多少参数。

Python允许您使用*操作符将任意数量的参数收集到一个形参中。接受任意数目实参的形参必须出现在函数定义的最后。

**操作符允许参数收集任意数量的关键字参数。

  • 收集任意数量的参数
def make_pizza(size, *toppings):    '''Make a pizza.'''    print('\nMaking a ' + size + ' pizza.')    print('Toppings:')    for topping in toppings:        print('- ' + topping)        # 用不同的配料做三个披萨。make_pizza('small', 'pepperoni')make_pizza('large', 'bacon bits', 'pineapple')make_pizza('medium', 'mushrooms', 'peppers',           'onions', 'extra cheese')
  • 收集任意数量的关键字参数
def build_profile(first, last, **user_info):    '''Build a user's profile dictionary.'''    # 使用所需的键构建字典。   profile = {'first': first, 'last': last}    # 添加任何其他键和值。    for key, value in user_info.items():        profile[key] = value    return profile# 创建两个具有不同类型信息的用户。user_0 = build_profile('albert', 'einstein',                       location='princeton')user_1 = build_profile('marie', 'curie',                       location='paris',                        field='chemistry')print(user_0)print(user_1)

8、模块

可以将函数存储在名为模块的单独文件中,然后将所需的函数导入包含主程序的文件中。这允许更纯粹的程序文件。(确保模块与主程序存储在同一个目录中。)

  • 在模块中存储函数
    文件:pizza.py
def make_pizza(size, *toppings):    '''Make a pizza.'''    print('\nMaking a ' + size + ' pizza.')    print('Toppings:')    for topping in toppings:        print('- ' + topping)
  • 导入整个模块
    文件:making_pizzas.py
    模块中的每个函数都可以在程序文件中找到。
import pizzapizza.make_pizza('medium', 'pepperoni')pizza.make_pizza('small', 'bacon', 'pineapple')
  • 导入特定函数
    程序文件中只有导入的函数可用。
from pizza import make_pizzamake_pizza('medium', 'pepperoni')make_pizza('small', 'bacon', 'pineapple')
  • 给一个模块一个别名
import pizza as pp.make_pizza('medium', 'pepperoni')p.make_pizza('small', 'bacon', 'pineapple')
  • 给一个函数一个别名
from pizza import make_pizza as mpmp('medium', 'pepperoni')mp('small', 'bacon', 'pineapple')
  • 从模块中导入所有函数

最好不要这样做,但当你在别人的代码中看到它时要认出它。它可能会导致命名冲突,从而导致错误。

from pizza import *make_pizza('medium', 'pepperoni')make_pizza('small', 'bacon', 'pineapple')

二、类

类定义对象的行为和对象可以存储的信息类型。类中的信息存储在属性中,属于类的函数称为方法。子类继承其父类的属性和方法。

类是面向对象编程的基础。类表示希望在程序中建模的真实世界的事物:例如,狗、汽车和机器人。可以使用一个类来创建对象,这些对象是狗、汽车和机器人的特定实例。类定义了整个对象类别可以拥有的一般行为,以及可以与这些对象关联的信息。

类可以相互继承——可以编写一个扩展现有类功能的类。这允许有效地编写各种情况下的代码。

命名约定: 在Python中,类名用驼峰大小写写,对象名用小写下划线写。包含类的模块仍然应该用小写下划线命名。

1、创建和使用一个类

考虑一下我们如何建模一辆汽车。我们会把什么信息和汽车联系起来,它会有什么行为?信息存储在称为属性的变量中,行为由函数表示。属于类的函数称为方法。

  • 创建一个小狗类
class Dog():    '''Represent a dog.'''    def __init__(self, name):        '''Initialize dog object.'''        self.name = name            def sit(self):       '''Simulate sitting.'''        print(self.name + ' is sitting.')        my_dog = Dog('Peso')print(my_dog.name + ' is a great dog!')my_dog.sit()
  • 创建一个车的类
class Car():    '''A simple attempt to model a car.'''    def __init__(self, make, model, year):        '''Initialize car attributes.'''        self.make = make        self.model = model        self.year = year        # 燃料容量和液位(加仑)。        self.fuel_capacity = 15        self.fuel_level = 0    def fill_tank(self):        '''Fill gas tank to capacity.'''        self.fuel_level = self.fuel_capacity        print('Fuel tank is full.')    def drive(self):        '''Simulate driving.'''        print('The car is moving.')
  • 从类中创建对象
 my_car = Car('audi', 'a4', 2016)
  • 访问属性值
print(my_car.make)print(my_car.model)print(my_car.year)
  • 调用方法
my_car.fill_tank()my_car.drive()
  • 创建多个对象
my_car = Car('audi', 'a4', 2016)my_old_car = Car('subaru', 'outback', 2013)my_truck = Car('toyota', 'tacoma', 2010)

2、修改属性

可以直接修改属性的值,也可以编写更仔细地管理值更新的方法。

  • 直接修改属性
my_new_car = Car('audi', 'a4', 2016)my_new_car.fuel_level = 5
  • 编写更新属性值的方法
def update_fuel_level(self, new_level):    '''Update the fuel level.'''    if new_level <= self.fuel_capacity:        self.fuel_level = new_level    else:    print('The tank can't hold that much!')
  • 编写递增属性值的方法
def add_fuel(self, amount):    '''Add fuel to the tank.'''    if (self.fuel_level + amount            <= self.fuel_capacity):        self.fuel_level += amount        print('Added fuel.')  else:    print('The tank won't hold that much!')

3、类继承

如果正在编写的类是另一个类的专门化版本,则可以使用继承。

当一个类从另一个类继承时,它会自动继承父类的所有属性和方法。子类可以自由地引入新的属性和方法,并覆盖父类的属性和方法。

要从另一个类继承,在定义新类时将父类的名称包含在括号中。

  • 子类的__init__()方法
# 小狗类的继承class SARDog(Dog):    '''Represent a search dog.'''    def __init__(self, name):        '''Initialize the sardog.'''        super().__init__(name)    def search(self):        '''Simulate searching.'''        print(self.name + ' is searching.')my_dog = SARDog('Willie')print(my_dog.name + ' is a search dog.')my_dog.sit()my_dog.search()
# 汽车类的继承class ElectricCar(Car):    '''A simple model of an electric car.'''    def __init__(self, make, model, year):        '''Initialize an electric car.'''        super().__init__(make, model, year)        # Attributes specific to electric cars.        # Battery capacity in kWh.        self.battery_size = 70        # Charge level in %.        self.charge_level = 0
  • 向子类添加新方法
class ElectricCar(Car):    --snip--    def charge(self):        '''Fully charge the vehicle.'''        self.charge_level = 100        print('The vehicle is fully charged.')
  • 使用子类方法和父类方法
my_ecar = ElectricCar('tesla', 'model s', 2016)my_ecar.charge()my_ecar.drive()
  • 重写父类方法
class ElectricCar(Car):    --snip--    def fill_tank(self):        '''Display an error message.'''        print('This car has no fuel tank!')

4、实例属性

一个类可以以对象作为属性。这允许类一起工作来建立复杂的情况。

  • 一个电池类
class Battery():    '''A battery for an electric car.'''    def __init__(self, size=70):        '''Initialize battery attributes.'''        # 容量以千瓦时计,充电水平以%计。        self.size = size        self.charge_level = 0    def get_range(self):        '''Return the battery's range.'''        if self.size == 70:            return 240        elif self.size == 85:      return 270
  • 使用实例作为属性
class ElectricCar(Car):    --snip--    def __init__(self, make, model, year):        '''Initialize an electric car.'''        super().__init__(make, model, year)        # 属性是电动汽车。        self.battery = Battery()    def charge(self):        '''Fully charge the vehicle.'''        self.battery.charge_level = 100        print('The vehicle is fully charged.')
  • 使用实例
my_ecar = ElectricCar('tesla', 'model x', 2016)my_ecar.charge()print(my_ecar.battery.get_range())my_ecar.drive()

5、导入类

只要添加了详细的信息和功能,类文件就会变得很长。为了帮助保持程序文件整洁,可以将类存储在模块中,并将所需的类导入到主程序中。

  • 在文件中存储类
    car.py
'''Represent gas and electric cars.'''class Car():    '''A simple attempt to model a car.'''    --snip--class Battery():    '''A battery for an electric car.'''    --snip--class ElectricCar(Car):    '''A simple model of an electric car.'''    --snip--
  • 从模块中导入单个类
    my_cars.py
from car import Car, ElectricCarmy_beetle = Car('volkswagen', 'beetle', 2016)my_beetle.fill_tank()my_beetle.drive()my_tesla = ElectricCar('tesla', 'model s', 2016)my_tesla.charge()my_tesla.drive()
  • 导入整个模块
import carmy_beetle = car.Car(        'volkswagen', 'beetle', 2016)my_beetle.fill_tank()my_beetle.drive()my_tesla = car.ElectricCar(        'tesla', 'model s', 2016)my_tesla.charge()my_tesla.drive()
  • 从模块中导入所有类
from car import *my_beetle = Car('volkswagen', 'beetle', 2016)
  • 在列表中存储对象

一个列表可以包含任意多的项,因此可以从一个类中创建大量对象并将它们存储在一个列表中。

下面是一个示例,演示如何创建一个出租车队,并确保所有的汽车都已准备就绪。

from car import Car, ElectricCar# 列出一个车队的名单。gas_fleet = []electric_fleet = []# 生产500辆汽油车和250辆电动汽车。for _ in range(500):    car = Car('ford', 'focus', 2016)    gas_fleet.append(car)for _ in range(250):    ecar = ElectricCar('nissan', 'leaf', 2016)    electric_fleet.append(ecar)# 给汽车加油,给电动汽车充电。for car in gas_fleet:    car.fill_tank()for ecar in electric_fleet:    ecar.charge()print('Gas cars:', len(gas_fleet))print('Electric cars:', len(electric_fleet))
(0)

相关推荐