入门必备!面向对象编程之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))