接上篇的《Python编程从入门到实践》第三版 学习笔记 中(5~7章节),今天更新 下 (8~9章节),也是我认为最重要且难啃的大骨头:函数和类(面向对象编程),废话不多说,下面我们直接开始,我会用尽量通俗形象的方式去分享这两章内容1.函数定义:使用def 定义一个函数,以冒号结尾,括号内为形参(parameter),在实际使用传入具体值属于实参(argument)3.返回值:使用return语句返回结果,函数可以返回任何类型数据,包括字典和列表。①普通列表参数:def func(items), 这里的items是普通的形参,调用此函数时我们需要提供一个完整打包好的数据集合(如列表或元组)函数内部拿到的names是你传进去的那个列表本身def greet_users(names): for name in names: print(f"Hello, {name}!")user_list = ["张三", "李四", "王五"]greet_users(user_list) # 内部拿到的 names 就是 ['张三', '李四', '王五'] 这个列表
②任意数量实参:def func(*args),这里的 * 是关键,它让函数具备了“吃进任意多个散装数据”的能力,适用于不确定要传入多少数据的情景,例如传入披萨配料时,你不确定用户要加多少种配料,函数内部会将你传进来的所有位置参数(散装的一个个值),自动封装打包成一个元组(tuple)def make_pizza(*toppings): print(toppings)make_pizza("芝士") # 内部 toppings 变成了 ("芝士",)make_pizza("芝士", "蘑菇", "培根") # 内部 toppings 变成了 ("芝士", "蘑菇", "培根")
③任意数量关键字实参:def func(**kwargs) (keyword arguments的缩写为kwargs) ,函数自动收集所有未匹配的关键字参数并打包为字典,形式是 key=value的参数,所有传入的关键字参数会被自动封装成一个字典# 任意数量关键字实参(**kwargs 创建字典)def build_profile(**user_info): return user_infobuild_profile(名字="小明", 年龄=18) # 内部 user_info 变成了 {'名字': '小明', '年龄': 18}
import module_name # 导入整个模块from module import func # 导入特定函数from module import func as f # 使用别名import module as m # 给模块指定别名
给模块指定别名后,在后面的调用中,只要使用此别名,后面用' . ' 带一个其内置的函数即可调用该模块内的函数。主程序:
模块程序:
输出:主程序
*toppings 中的 * 表示接收任意数量的位置实参,在函数内部它们是元组**user_info 中的 ** 表示接收任意数量关键字实参在函数内部它们是字典默认值参数(country='中国')让函数调用更灵活将函数放入单独模块是良好的编程习惯,通过 import 引入可提高代码复用性。类(Class)是面向对象编程(OOP)的核心,用来模拟现实世界中的事物类(Class):描述一类事物的模板,定义了这类事物的属性和行为对象(Object):根据类创建的具体实例。根据类创建对象的过程叫实例化通俗理解:类就像汽车设计稿,它定义了汽车的发动机,变速箱的运作模式,定义了启动,刹车等功能,及其外观属性,等等,但它不是汽车,只是描述了汽车应该是什么样子,具备哪些功能及属性,真正路上跑的蓝色兰博基尼,红色牧马人 这些是具体的对象,他们具备汽车的设计稿所描述的功能及属性(颜色、品牌等),只是各自的属性值不一样。2.创建并使用类(class):Python中首字母大写的名称指的是类,创建全新的类不需要加括号,文档字符串对类的功能做描述,类中的函数称为方法说明1:self就是实例本身(如my_dog),Python自动将实例作为第一个参数传递,self只是一个形参名,用于在方法内部引用当前实例,这个设计是面向对象编程的核心:方法知道自己在操作哪个具体对象,从而实现封装和多态。说明2:默认方法__init__()中,定义变量self.name = name 等号右边的是形参name,它接收传入的值,等号左边的是实例属性self.name,它是当前实例的属性,所以这行代码的意思是将传入的name参数的值赋值给实例的属性self.name (self.name = name获取与形参name相关联的值,并将其关联到当前创建的实例,这种通过实例访问的变量称为属性(attribute))例题1:创建一个模拟小狗的类,属性包括姓名和年龄,同时类中还要包括‘坐下’和‘打滚’两个可执行的方法。最后创建几个实例,调用这个类中的属性和方法。①__init__() 方法是默认方法,只要调用该类就会自动运行该方法;②self可以理解成实例本身,如上例中的my_dog,其自动作为第一个参数传递③默认方法__init__()中,定义变量self.name = name 左边self代表实例,self.name也就是实例的一个属性,右边name是形参,它可以关联到具体的值(实参)3.类的继承:子类继承父类的所有属性和方法,并可添加自己的特性①创建子类时,父类必须包含在当前文件中,且位于子类前面,import 导入的也算③super()是一个特殊函数,让你能够调用父类的方法,让Python调用父类的默认方法__init__(),从而让子类的实例包含父类默认方法定义的所有属性④父类也称超类(superclass),函数名super由此得名。例题2:在上面例题1的前提下,创建它的一个子类:电子狗,增加一个唱歌的方法,最后再调用它。创建一个名为 restaurant.py 的程序:
创建一个名为 Restaurant 的类:
__init__ 方法应包含属性:restaurant_name、cuisine_type
设置默认属性 number_served = 0
定义方法 describe_restaurant():打印餐厅名称和菜系类型
定义方法 open_restaurant():打印一条消息说明餐厅正在营业
定义方法 set_number_served(n):将就餐人数设为 n
定义方法 increment_number_served(n):将就餐人数增加 n
创建一个 IceCreamStand 类,继承自 Restaurant:
创建 Restaurant 的实例,调用各方法,修改就餐人数
创建 IceCreamStand 的实例,调用父类方法和子类特有方法
①.这里题目要求“设置默认属性number_served = 0”意味创建餐厅时人数总是从0开始,逻辑上餐厅初始状态人数肯定是0这是一个内部状态,不是外部传入的参数,也就不能写在__init__()括号内,写进去就是可传入参数,后续通过set_number_served() 和 increment_number_served() 方法来修改人数是对属性的行为控制,也符合逻辑。②.这里的number_served在父类中已经在默认方法内作为非外部传入参数设置为默认值0了,因此子类调用时应遵循父类默认方法。同时使用 super().__init__() 调用父类方法时,不需要传入 self 参数,Python 会自动处理,我一开始犯的错就是写成 super().__init__(self, ...),这里子类默认方法中已定义了self,父类中不需要重复定义。③.display_flavors 应该打印实例自身的 flavors 列表(self.flavors),而不是通过参数传入。同时需要在 __init__ 中接收 flavors 参数并赋值给 self.flavors结果输出
本篇小结:这两章主要为函数的创建和使用方法以及类的创建和使用方法,类的相关知识引出了面向对象编程这一重要的概念,我们要理解面向对象编程就必须理解类的使用逻辑,用一句话概括就是:类是设计稿,对象是实物;属性是特征,方法是行为;万物皆对象。