Python零基础入门(九)——函数,类和对象

系列文章目录

个人简介:机电专业在读研究生,CSDN内容合伙人,博主个人首页
Python入门专栏:《Python入门》欢迎阅读,一起进步!🌟🌟🌟
码字不易,如果觉得文章不错或能帮助到你学习,可以点赞👍收藏📁评论📒+关注哦!😊😊😊


导语

欢迎来到 Python 零基础入门系列的第九篇!前面的文章中,已经介绍了 Python 的基本语法、控制流程和数据类型等内容。这一篇博客将继续探索 Python 编程的核心概念——函数,类和对象。

函数

Python 编程中,函数的应用非常广泛。在前面我们已经多次接触过函数。如,用于输出的 print() 函数、用于输入的 input() 函数,以及用于生成一系列整数的 range() 函数。这些都是 Python 内置的标准函数,可以直接使用。除了可以直接使用的标准函数外,Python 还支持自定义函数。即通过将一段有规律的、 重复的代码定义为函数,来达到一次编写多次调用的目的。使用函数可以提高代码的重复利用率。

函数的创建与调用

在 Python 中,def 关键字用来定义函数。语法如下:

def fuctionname(parameter1, parameter2, ...):
    ['''comment''']
    [fuctionbody]

‘comments’:可选参数,表示为函数指定注释,注释的内容通常是说明该函数的功能、要传递的参数的作用等。如果指定了 comments 参数,那么在调用函数时,输入函数名称及左侧的小括号时,就会显示该函数的帮助信息。这些帮助信息就是通过定义的注释提供的。

函数的定义由函数名参数列表函数体组成。函数名应该是一个描述性的名字,体现函数的作用。参数列表也可以不指定参数,不指定则表示该函数没有参数。在调用时,也不指定参数。

如果想定义一个什么也不做的空函数,可以使用 pass 语句作为占位符。在 Python3.x 版本中,允许在可以使用表达式的任何地方使用 ... (3 个连续的点号)来省略代码,由于省略号自身什么都不做,因此,可以当作是 pass 语句的一种替代方案。

调用函数也就是执行函数。调用函数时,使用函数名后加上括号来调用。在括号内,传递函数需要的参数。参数的数量和类型应该与函数定义中的参数列表一致。

参数传递

位置参数与关键字参数

函数可以接受参数,参数是函数的输入。Python 中有两种类型的参数:位置参数关键字参数。位置参数是必须按照参数在函数定义中的顺序传递的;而关键字参数则是通过指定参数名来传递的,需要将参数名写对。

def greet(name, greeting):
    return f"{greeting}, {name}!"

# 位置参数传递
print(greet("Alice", "Hello"))  # 输出:Hello, Alice!

# 关键字参数传递
print(greet(greeting="Hi", name="Bob"))  # 输出:Hi, Bob!

为参数设置默认值

调用函数时,如果没有指定某个参数将抛出异常,而为函数参数设置默认值允许我们在调用函数时不传递该参数,直接使用参数的默认值。这在某些情况下非常有用,特别是当函数有一些常用的选项时,可以避免频繁传递相同的值。

def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

# 不传递greeting参数时,将使用默认值"Hello"
greet("Alice")  # 输出:Hello, Alice!

# 传递greeting参数时,将使用传递的值
greet("Bob", "Hi")  # 输出:Hi, Bob!

上面的例子为 greeting 参数设置了默认值 “Hello”。当调用 greet 函数时,如果不传递 greeting 参数,它将使用默认值,否则将使用传递的值。

  • 注意:在定义函数时,指定默认的参数必须在所有参数的最后,否则将产生语法错误。
  • 在 Python 中,可以使用 函数名.__defaults__ 查看函数的默认值参数的当前值,其结果是一个元组。

可变参数

在 Python 中,还可以定义可变参数。可变参数也称为不定长参数,即传入函数中的实际参数可以是 0 个、一个、两个到任意个。

定义可变参数时,主要有两种形式:一种是 *args,另一种是 **kwargs。下面分别进行介绍。

  • *args

    这种形式表示接收任意多个位置参数并将其放到一个元组中。示例代码如下:

    def printplayer(*name):		#定义输出我喜欢的NBA球员的函数
        print('\n我喜欢的NBA球员有: ')
        for item in name:
            print(item)		#输出球员名称
            
    printplayer('邓肯')
    printplayer('乔丹','科比','詹姆斯','库里')
    '''
    输出:
    我喜欢的NBA球员有: 
    邓肯
    
    我喜欢的NBA球员有: 
    乔丹
    科比
    詹姆斯
    库里
    '''
    

    如果想要使用一个已经存在的列表作为函数的可变参数,可以在列表的名称前加 *

  • **kwargs

    这种形式表示接收任意多个关键字参数,并将其放到一个字典中。示例代码如下:

    def printsign(**sign):      # 定义输出姓名和绰号的函数
        for key, value in sign.items():     # 遍历字典
            print("[" + key + "]的绰号是:" + value)    # 输出组合后的信息
    
    printsign(邓肯='石佛')
    printsign(吉诺比利='妖刀',帕克='跑车',邓肯='石佛')
    '''
    输出:
    [邓肯]的绰号是:石佛
    [吉诺比利]的绰号是:妖刀
    [帕克]的绰号是:跑车
    [邓肯]的绰号是:石佛
    '''
    

    如果想要使用一个已经存在的字典作为函数的可变参数,可以在字典的名称前加 **

返回值

函数可以通过 return 语句返回一个值。如果没有指定 return 语句,函数将默认返回 None。示例如下:

def add(a, b):
    return a + b

result = add(3, 5)
print(result)  # 输出:8

注意:无论 return 语句出现在函数的什么位置,只要得到执行,就会直接结束函数的执行。

变量的作用域

在函数中定义的变量拥有自己的作用域。这意味着,函数内部的变量只能在函数内部访问,称为局部变量;而函数外部的变量则能在函数内部直接访问,称为全局变量。

def my_function():
    x = 10
    print(x)

my_function()  # 输出:10

# 在函数外部访问函数内部的变量将引发错误
print(x)  # NameError: name 'x' is not defined

当局部变量与全局变量重名时,对函数体的变量进行赋值后,不影响函数体外的变量。但是在实际开发时,不建议这么做。

在函数体内定义,并且使用 global 关键字修饰后的变量也可以变为全局变量。在函数体外也可以访问到该变量,并且在函数体内还可以对其进行修改。

匿名函数

Python 支持使用 lambda 关键字创建匿名函数,也称为 lambda 函数。lambda 表达式的首要用途是指定短小的回调函数,它们是一行代码的函数。如下所示:

multiply = lambda x, y: x * y
result = multiply(3, 4)
print(result)  # 输出:12

使用 lambda 表达式时,参数可以有多个,用逗号 “,” 分隔,但是表达式只能有一个,即只能返回一个值。而且也不能出现其他非表达式语句(如 forwhile)。

常见Python内置函数

除了 print()用于打印输出、range()用于生成一系列数字等,Python 还提供了许多内置函数,包括如下:

内置函数功能内置函数功能
abs()返回一个数的绝对值len()返回一个对象的长度(元素个数)
max()返回一组数或可迭代对象中的最大值min()返回一组数或可迭代对象中的最小值
sum()返回一组数或可迭代对象中所有元素的和round()对一个数进行四舍五入
str()将一个对象转换为字符串int()将一个对象转换为整数
float()将一个对象转换为浮点数list()将一个可迭代对象转换为列表
tuple()将一个可迭代对象转换为元组set()将一个可迭代对象转换为集合
dict()创建一个字典sorted()对一个可迭代对象进行排序
format()格式化输出文本zip()将多个可迭代对象打包为元组的列表
enumerate()枚举一个可迭代对象的元素及其索引map()对一个可迭代对象的每个元素应用一个函数
filter()根据指定条件过滤一个可迭代对象的元素any()判断一个可迭代对象中是否至少有一个元素为True
all()判断一个可迭代对象中的所有元素是否都为Truecallable()判断一个对象是否可调用(函数或方法)
type()返回一个对象的类型dir()返回一个对象的所有属性和方法
getattr()获取一个对象的属性值setattr()设置一个对象的属性值
hasattr()判断一个对象是否有指定的属性delattr()删除一个对象的属性
open()打开文件并返回文件对象input()从用户输入读取一行文本

类与对象

面向对象概述

面向对象编程(Object-Oriented Programming,OOP)是一种重要的编程范式,它将数据和操作数据的方法封装在一起,形成对象。OOP 的核心思想是将现实世界的事物抽象成一个个对象,并通过定义类来描述这些对象的特征和行为。类是对象的蓝图,而对象是类的实例。在 Python 中,一切皆为对象。

OPP 有三大基本特征,分别是封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。

  • 封装(Encapsulation):

    • 封装是指将数据和操作(方法)组合在一起形成一个独立的单元,对外部隐藏内部实现细节。通过封装可以保护数据的安全性,只能通过预定义的接口(方法)访问和修改数据。
    • 封装可以使代码更加模块化和可维护,减少了对外部的依赖性,提高了代码的可重用性和安全性。
  • 继承(Inheritance):

    • 继承是指一个类(子类)可以继承另一个类的属性和方法,从而避免了重复编写相同的代码。被继承的类称为父类或基类,继承的类称为子类或派生类。

    • 通过继承,子类可以拥有父类的属性和方法,并且可以在此基础上进行扩展或修改。

  • 多态(Polymorphism):

    • 多态是指同一种操作或方法可以应用于不同类型的对象,而产生不同的行为。通过多态,可以以统一的方式对不同的对象进行操作,无需关心对象具体的类型。
    • 多态性使得程序更加灵活和可扩展,可以通过定义抽象类、接口或基类来实现统一的方法签名,然后由不同的子类实现自己的具体行为。这样可以提高代码的可读性、可维护性和可扩展性。

这三大特性是面向对象编程的基础,共同支持着代码的重用、封装和抽象,使得我们能够以更高效和结构化的方式进行软件开发。

类的定义与使用

定义一个类需要使用 class 关键字,类中可以包含属性(也称为成员变量)和方法(也称为成员函数)。class 语句本身并不创建该类的任何实例。所以在类定义完成以后,可以创建类的实例,即实例化该类的对象。通过创建类的实例,可以使用该类中定义的属性和方法。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_hello(self):
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")

# 创建类的实例
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)

# 调用实例方法
person1.say_hello()  # 输出:Hello, my name is Alice and I am 25 years old.
person2.say_hello()  # 输出:Hello, my name is Bob and I am 30 years old.

__init__() 方法

在创建类后,类通常会自动创建一个__init__() 方法。该方法是一一个特殊的方法,每当创建-一个类的新实例时,Python 都会自动执行它。__init__() 方法必须包含一个 self 参数,并且必须是第一个参数。self 参数是一个指向实例本身的引用,用于访问类中的属性和方法。在方法调用时会自动传递实际参数 self。因此,当 __init__() 方法只有一个参数时,在创建类的实例时,就不需要指定实际参数了。

class Geese:
    ''' 大雁类 '''
    def __init__(self):     # 构造方法
        print("我是大雁类!")
wildGoose = Geese()         # 创建大雁类的实例
'''
输出:
我是大雁类! 
'''

访问限制

在类的内部可以定义属性和方法,而在类的外部则可以直接调用属性或方法来操作数据,从而隐藏了类内部的复杂逻辑。但是 Python 并没有对属性和方法的访问权限进行限制。为了保证类内部的某些属性或方法不被外部所访问,可以在属性或方法名前面添加单下划线(_foo)、双下划线(__ foo)或首尾加双下划线(__foo__), 从而限制访问权限。其中,单下划线、双下划线、首尾双下划线的作用如下:

  • __ foo__:首尾双下划线表示定义特殊方法,一般是系统定义名字,如 __init__()
  • _foo: 以单下划线开头的表示 protected(保护)类型的成员,只允许类本身和子类进行访问,但不能使用 from module import *语句导入。
  • __foo:双下划线表示 private(私有)类型的成员,只允许定义该方法的类本身进行访问,而且也不能通过类的实例进行访问,但是可以通过 类的实例名.类名_xxx 方式访问。

属性

这里介绍的属性与上面介绍的类属性和实例属性不同。上面介绍的属性将返回所存储的值。而这里要介绍的属性则是一种特殊的属性,访问它时将计算它的值。另外,该属性还可以为属性添加安全保护机制。下面分别进行介绍。

创建用于计算的属性

在 Python 中,可以通过 @property(装饰器)将一个方法转换为属性,从而实现用于计算的属性。将方法转换为属性后,可以直接通过方法名来访问方法,而不需要再添加一对小括号 “()",这样可以让代码更加简洁。

通过 @property 创建用于计算的属性的示例如下:

class Rect:
    def __init__(self,width,height):
        self.width = width		#矩形的宽
        self.height = height	#矩形的高
    @property		            #将方法转换为属性
    def area(self):		        #计算矩形的面积的方法
        return self.width*self.height		#返回矩形的面积
rect = Rect(800,600)		                #创建类的实例
print("面积为: ",rect.area)		        #输出属性的值
'''
输出:
面积为:  480000
'''

注意:通过 @property 转换后的属性不能重新赋值。如果对其重新赋值,将抛出 AttributeError 异常。

为属性添加安全保护机制

在 Python 中,默认情况下,创建的类属性或者实例,是可以在类体外进行修改的,如果想要限制其不能在类体外修改,可以将其设置为私有的,但设置为私有后,在类体外也不能获取它的值。如果想要创建一个 可以读取,但不能修改的属性,那么可以使用 @property 实现只读属性。

class TVshow:               #定义电视节目类
    def __init__(self,show):
        self.__show = show
    @property               #将方法转换为属性
    def show(self):         #定义show()方法
        return self.__show  #返回私有属性的值
tvshow = TVshow("正在播放《封神》")     #创建类的实例
print("默认:",tvshow.show)            #获取属性值
'''
输出:
默认: 正在播放《封神》
'''

通过上面的方法创建的 show 属性是只读的。

继承

在编写类时,并不是每次都要从空白开始。当要编写的类和另一个已经存在的类之间存在一定的继承关系时,就可以通过继承来达到代码重用的目的,提高开发效率。被继承的类称为父类或基类,继承的类称为子类或派生类。子类可以重写父类的方法或添加新的方法。以下是一个示例:

class Animal:
    def __init__(self, name):
        self.name = name
    
    def sound(self):
        pass

class Dog(Animal):
    def sound(self):
        return "Woof!"

class Cat(Animal):
    def sound(self):
        return "Meow!"

d = Dog("Buddy")
print(d.sound())  # 输出:Woof!

c = Cat("Kitty")
print(c.sound())  # 输出:Meow!

在上述代码中,定义了一个名为 Animal 的父类,它具有一个属性 name 和一个未实现的方法 sound()。然后,定义了两个子类 DogCat,它们继承了父类 Animal 并实现了自己的 sound() 方法。通过创建子类的实例,可以调用其方法并输出相应的结果。

注意:在派生类中定义 __init__() 方法后,不会再自动调用基类的 __init__() 方法。

结语

以上是关于函数、类和对象的知识介绍。希望本篇教程能够帮助你更好地理解和应用 Python 编程语言的相关概念和技巧。