首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【python】零基础入门(3/3):函数 || 类

【python】零基础入门(3/3):函数 || 类

原创
作者头像
魏言
发布2025-03-05 10:41:19
发布2025-03-05 10:41:19
1471
举报

10. 函数

10.1. 函数的基本使用

先定义一个函数,需要使用def关键字,然后跟函数名,函数名可用字母、数字以及下划线,不能出现空格和汉字。

然后调用这个函数,有参数就传参,没有参数就不传。

代码语言:python
复制
def say_hello():
    print('你好!')
    
say_hello()

输出结果为:

代码语言:txt
复制
你好!

10.2. 位置参数

调用函数时传入值的顺序,按照定义函数时的参数顺序,不可错序,传入值和函数参数的数量也要相同。

代码语言:python
复制
def say_hello(name, age):
    print(f'你好,我是{name},今年是{age}岁。')
    
say_hello('weiyan', 16)
say_hello(17, 'weiyan')

输出结果为:

代码语言:txt
复制
你好,我是weiyan,今年是16岁。
你好,我是17,今年是weiyan岁。

10.3. 关键字参数

调用函数传参数时指定了每个参数的值,此时参数的顺序可以随意,但是传入值和函数参数的数量要相同。

代码语言:python
复制
def say_hello(name, age):
    print(f'你好,我是{name},今年是{age}岁。')

say_hello(age=18, name='weiyan')

输出结果为:

代码语言:txt
复制
你好,我是weiyan,今年是18岁。

10.4. 默认参数

定义函数时可以给部分参数设置默认值,调用函数时如果传了值,就使用传的值,如果没传,就使用默认值。

代码语言:python
复制
def say_hello(name, age=19):
    print(f'你好,我是{name},今年是{age}岁。')
    
say_hello('weiyan')
say_hello('youzi', 17)

输出结果为:

代码语言:txt
复制
你好,我是weiyan,今年是19岁。
你好,我是youzi,今年是17岁。

注意:定义函数时,设置默认参数,应设置为不可变对象如None、整数、浮点数、字符串等,不要使用可变对象如列表、字典。因为默认参数是在定义函数时计算,而不是在调用函数时计算,如果使用可变对象作为默认参数,那么多次调用这个函数时,这个可变对象的值会保留下来,对后续调用函数产生影响,示例如下:

代码语言:python
复制
def my_colors(name, color, colors=[]):
    colors.append(color)
    print(f'你好,我叫{name},我喜欢的颜色是:{colors}。')

my_colors('魏言', 'red')
my_colors('柚子', 'blue')
my_colors('东东', 'dark')

输出结果为:

代码语言:txt
复制
你好,我叫魏言,我喜欢的颜色是:['red']。
你好,我叫柚子,我喜欢的颜色是:['red', 'blue']。
你好,我叫东东,我喜欢的颜色是:['red', 'blue', 'dark']。

可见,输出结果并不符合,原因是每次调用函数,列表colors的值都保留下来,对后续调用产生影响。

10.5. 可变长度参数

可变长度参数,也叫可变参数。定义函数时,使用*args可以接收任意数量的位置参数,使用**kwargs可以接收任意数量的关键字参数。

使用*args可以接收任意数量的位置参数,传入的参数会以元组的形式存储。

代码语言:python
复制
def say_hello(*args):  # 使用*args可以接收任意数量的位置参数
    for name in args:
        print(f'{name},', end='')
    print(f'你好!')
    
say_hello('weiyan', 'youzi')

输出结果为:

代码语言:txt
复制
weiyan,youzi,你好!

使用**kwargs可以接收任意数量的关键字参数,传入的参数会以字典的形式存储。

代码语言:python
复制
def infor(name, **kwargs):  # 使用**kwargs可以接收任意数量的关键字参数
    print(f'{name}是{kwargs["age"]}岁,目前住在{kwargs["city"]}。')

infor('weiyan', age=17, city='深圳')

输出结果为:

代码语言:txt
复制
weiyan是17岁,目前住在深圳。

10.6. 返回值

在函数中,使用return某个对象,作为函数的结果。

注意,return要放在函数中的最后,当代码运行到return时,后面的代码就不会运行了。

代码语言:python
复制
def say_hello(name, age=19):
    words = '你好,我是' + name + ',今年是' + str(age) + '岁。'
    return words

words = say_hello('weiyan')
print(words)

输出结果为:

代码语言:txt
复制
你好,我是weiyan,今年是19岁。

10.7. lambda 函数

lambda 函数,即匿名函数,通常用于比较简单的操作。

lambda 函数的简单使用如下第一个函数,第二个函数是与它有同等效果的:

代码语言:python
复制
square = lambda n: n ** 2
print(square(5))

def square(n):
    return n ** 2
    
print(square(5))

输出结果为:

代码语言:txt
复制
25
25

10.8. 高阶函数

可以把函数名也作为参数传入函数。

代码语言:python
复制
def higher_fun(m, n):
    return m(n)

def fun(n):
    return n ** 2

num = higher_fun(fun, 5)
print(num)

输出结果为:

代码语言:txt
复制
25

11. 类

11.1. 类的基本使用

使用 class 关键字创建类,并创建它的对象。比如说,“人类”作为一个类,那么“哪吒”作为人类中的个体,就可以通过“人类”这个类创建一个实例,来描述“哪吒”。

以下,创建了一个非常简单类 Person,并创建一个它的实例 nezha,然后调用一个实例方法,打印了一句话。

代码语言:python
复制
class Person:
    def __init__(self, name):
        self.name = name

    def print_infor(self):
        print(f'你好,我的名字是:{self.name}。')

nezha = Person('哪吒')
nezha.print_infor()

输出结果为:

代码语言:txt
复制
你好,我的名字是:哪吒。

11.2. 比较完整的类结构

作为一个比较完整的类,它除了类名,可能还有类属性,实例属性,类方法,实例方法,静态方法。

示例如下:

代码语言:python
复制
class Person:
    meals = 2  # 类属性
    hands = 2

    def __init__(self, name, age):
        self.name = name  # 实例属性
        self.age = age

    def print_infor(self):  # 实例方法1
        print(f'你好,我的名字是:{self.name}。')

    def update_age(self, num):  # 实例方法2
        self.age = num

    @classmethod
    def update_meals(cls, num):  # 类方法
        cls.meals = num

    @staticmethod
    def say_hello():  # 静态方法
        print('Hello!')

其中,函数 __init__() 是类的构造函数,在创建类的实例时进行初始化操作。

注:__init__() 函数是每个类都有的,但不是必须写,有时候我们创建简单的类,没有写 __init__() 函数,代码也能正常运行,那是因为 python 会默认给提供一个无参数的构造函数。

11.3. 类属性、实例属性、类方法、实例方法

类属性:是描绘整个类的,在定义类时设置。

实例属性:实例属性是描绘单独某个实例的,定义类时在 __init__() 函数中设置。

区别:修改类属性,会对所以这个类的所有实例生效;而修改实例属性,只会对单个的实例生效。可以通过实例获取类属性,但是不能通过类去获取实例属性。

类方法:类方法需要使用 @classmethod 装饰,它用来修改和访问类属性,第一个参数是作为类本身的 cls

实例方法:实例方法不需要额外修饰,它用来修改和访问实例属性,第一个参数是作为实例本身的 self。

区别:实例可以调用类方法,但是不能用类去调用实例方法。

类属性和实例属性的修改方式:

1.类属性,可以通过类直接修改类属性,也可以通过实例调用类方法修改类属性。修改后,对所有这个类的实例都有效。

2.实例属性,可以通过实例直接修改实例属性,也可以通过实例调用实例方法修改实例属性。修改后,只对当前这个实例有效。

示例如下,先创建两个实例,然后用不同方法修改了类属性和实例属性,然后展示修改结果:

代码语言:python
复制
class Person:
    meals = 2  # 类属性
    hands = 2

    def __init__(self, name, age):
        self.name = name  # 实例属性
        self.age = age

    def print_infor(self):  # 实例方法1
        print(f'你好,我的名字是:{self.name}。')

    def update_age(self, num):  # 实例方法2
        self.age = num

    @classmethod
    def update_meals(cls, num):  # 类方法
        cls.meals = num

print('修改前:')
nezha = Person('哪吒', 3)
weiyan = Person('魏言', 16)
print(f'实例:名字是{nezha.name}, {nezha.age}岁, 每天吃{nezha.meals}餐,有{nezha.hands}只手。')
print(f'实例:名字是{weiyan.name}, {weiyan.age}岁, 每天吃{weiyan.meals}餐, 有{weiyan.hands}只手。')
print(f'类:每天吃{Person.meals}餐, 有{Person.hands}只手。')

Person.hands = 6  # 通过类直接修改类属性,对所有实例生效
nezha.update_meals(3)  # 通过实例调用类方法修改类属性,对所有实例生效
weiyan.age = 17  # 通过实例之间修改实例属性,对当前实例生效
nezha.update_age(4)  # 通过实例调用实例方法修改实例属性,对当前实例生效
print('修改后:')
print(f'实例:名字是{nezha.name}, {nezha.age}岁, 每天吃{nezha.meals}餐,有{nezha.hands}只手。')
print(f'实例:名字是{weiyan.name}, {weiyan.age}岁, 每天吃{weiyan.meals}餐, 有{weiyan.hands}只手。')
print(f'类:每天吃{Person.meals}餐, 有{Person.hands}只手。')

输出结果为:

代码语言:txt
复制
修改前:
实例:名字是哪吒, 3岁, 每天吃2餐,有2只手。
实例:名字是魏言, 16岁, 每天吃2餐, 有2只手。
类:每天吃2餐, 有2只手。
修改后:
实例:名字是哪吒, 4岁, 每天吃3餐,有6只手。
实例:名字是魏言, 17岁, 每天吃3餐, 有6只手。
类:每天吃3餐, 有6只手。

特殊情况:类属性和实例属性重名,类方法和实例方法重名

如果类属性和实例属性重名,实例属性会把这个实例对应的类属性覆盖掉,即访问时得到的值是实例属性的值。

如果类方法和实例方法重名,编辑器会提示报错,如果强行运行,后者会生效,前者无效。

代码语言:python
复制
class Person:
    hands = 2

    def __init__(self, name):
        self.name = name  # 实例属性
        self.hands = 6

    def print_infor(self):  # 实例方法1
        print(f'你好,我的名字是:{self.name}。')

    def update_hands(self, num):  # 实例方法2
        self.hands = num
        print(66666)

    @classmethod
    def update_hands(cls, num):  # 类方法
        cls.hands = num

print('修改前:')
nezha = Person('哪吒')
print(f'实例:名字是{nezha.name}, 有{nezha.hands}只手。')
print(f'类:有{Person.hands}只手。')

Person.hands = 3  # 通过类直接修改类属性
nezha.update_hands(4)  # 通过实例调用类方法修改类属性
nezha.hands = 7  # 通过实例直接修改实例属性
nezha.update_hands(8)  # 通过实例调用实例方法修改实例属性
print('修改后:')
print(f'实例:名字是{nezha.name}, 有{nezha.hands}只手。')
print(f'类:有{Person.hands}只手。')

输出结果为:

代码语言:txt
复制
修改前:
实例:名字是哪吒, 有6只手。
类:有2只手。
修改后:
实例:名字是哪吒, 有7只手。
类:有8只手。

11.4. 静态方法

当类中需要一个函数,而它的使用,不访问类属性也不访问实例属性,就像一个普通的函数那样,这时候可以创建一个静态方法。

静态方法使用 staticmethod 关键字装饰,对参数格式无特殊要求。可以使用实例来调用静态方法,不可使用类来调用。

代码语言:python
复制
class Person:
    hands = 2

    def __init__(self, name):
        self.name = name

    @staticmethod
    def say_hello():  # 静态方法
        print('Hello!')

nezha = Person('哪吒')
nezha.say_hello()

输出结果为:

代码语言:txt
复制
Hello!

注意:静态方法不能访问类属性,也不能访问实例属性,这是它和类方法、实例方法的最大区别。

11.5. 单继承

创建一个子类,这个子类继承一个父类,这就是单继承。

在子类中,要使用 super().__init__() 调用父类构造函数,使父类的属性初始化。

子类会继承父类的属性和方法,还可以对这些属性和方法进行重写,子类的实例可以调用它们。这样大大提高了代码的复用率。

代码语言:python
复制
class Person:
    hands = 2

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

    def print_infor(self):
        print(f'你好,我的名字是:{self.name}。')

class Male(Person):
    pass

nezha = Male('哪吒', 3)
print(f'实例属性:名字是{nezha.name}, {nezha.age}岁,有{nezha.hands}只手。')
print(f'父类属性:有{Person.hands}只手。')
print(f'子类属性:有{Male.hands}只手。')
nezha.print_infor()

输出结果为:

代码语言:txt
复制
实例属性:名字是哪吒, 3岁,有2只手。
父类属性:有2只手。
子类属性:有2只手。
你好,我的名字是:哪吒。

在上述代码中,我们创建了一个子类 Male,继承父类 Person,子类中什么也没有,但是用子类创建的实例,它可以直接调用类属性、实例属性和方法,这是因为子类继承了父类,父类所有的类属性、类方法、实例属性、实例方法、静态方法,全都继承了。

11.6. 子类重写属性和方法

对于父类中已有的属性和方法,无论是类属性、实例属性、类方法、实例方法或者是静态方法,子类中如果重写了同名的属性、方法,就会覆盖掉前者。

代码语言:python
复制
class Person:
    hands = 2

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

    def print_infor(self):
        print(f'你好,我的名字是:{self.name}。')

class Male(Person):
    hands = 6

    def __init__(self, name, age):
        super().__init__(name, age)
        self.name = name
        self.age = age + 1

    def print_infor(self):  # 实例方法1
        print(f'你好,哈哈哈哈哈,我的名字是:{self.name}。')

nezha = Male('哪吒', 3)
print(f'实例属性:名字是{nezha.name}, {nezha.age}岁,有{nezha.hands}只手。')
nezha.print_infor()

输出结果为:

代码语言:txt
复制
实例属性:名字是哪吒, 4岁,有6只手。
你好,哈哈哈哈哈,我的名字是:哪吒。

在这里,我们在子类中,把类属性 hands 从2改成了6,实例属性 age 加1,实例方法 print_infor() 也进行了重写。最后创建实例时,调用这些属性、方法,发现生效的是重写后的。

所以,修改父类属性和方法时,子类中也会生效,但是如果子类中重写了这个属性和方法,那父类的修改就不会影响子类和子类的实例。

11.7. 多继承

对于一个子类,在必要时,它可能继承多个父类,这就是多继承。

多继承时,子类会继承所有父类的属性和方法,子类的实例可以调用它们。

代码语言:python
复制
class PersonA:
    def say_hello1(self):
        print('Hello A')

class PersonB:
    def say_hello2(self):
        print('Hello B')

class PersonC(PersonA, PersonB):
    def say_hello3(self):
        print('Hello C')

weiyan = PersonC()
weiyan.say_hello1()
weiyan.say_hello2()
weiyan.say_hello3()

输出结果为:

代码语言:txt
复制
Hello A
Hello B
Hello C

属性和方法重名的情况

当属性和方法重名时,会优先生效子类自己的属性和方法;

如果子类中没有重写,是父类互相重名,则按继承的顺序,排在前面的父类生效。

代码语言:python
复制
class PersonA:
    def say_hello(self):
        print('Hello A')

class PersonB(PersonA):
    def say_hello(self):
        print('Hello B')

class PersonC(PersonA):
    def say_hello(self):
        print('Hello C')

class PersonD(PersonC, PersonB):
    pass

输出结果为:

代码语言:txt
复制
Hello C

当多继承时,父类之间存在父子关系

多继承中,如果父类之间存在父子关系,或者祖孙关系,那么继承时,“辈分”大的,要写在后面。

错误示范:

代码语言:python
复制
class PersonA:
    def say_hello(self):
        print('Hello A')

class PersonB(PersonA):
    def say_hello(self):
        print('Hello B')

class PersonC(PersonA, PersonB):
    pass

weiyan = PersonC()
weiyan.say_hello()

输出结果为:

代码语言:txt
复制
Traceback (most recent call last):
  File "F:\python_scripts\isli_tools\base\base.py", line 302, in <module>
    class PersonC(PersonA, PersonB):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases PersonA, PersonB

正确做法:

代码语言:python
复制
class PersonA:
    def say_hello(self):
        print('Hello A')

class PersonB(PersonA):
    def say_hello(self):
        print('Hello B')

class PersonC(PersonB, PersonA):
    pass

weiyan = PersonC()
weiyan.say_hello()

输出结果为:

代码语言:txt
复制
Hello B

假如说,有类A,它有子类B和C,其中类C有子类D,那么现在新建一个类,它通过多继承的方式继承B和D,是否需要考虑顺序呢?答案是不需要,因为他俩互相之间不存在父子关系。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 10. 函数
    • 10.1. 函数的基本使用
    • 10.2. 位置参数
    • 10.3. 关键字参数
    • 10.4. 默认参数
    • 10.5. 可变长度参数
    • 10.6. 返回值
    • 10.7. lambda 函数
    • 10.8. 高阶函数
  • 11. 类
    • 11.1. 类的基本使用
    • 11.2. 比较完整的类结构
    • 11.3. 类属性、实例属性、类方法、实例方法
    • 11.4. 静态方法
    • 11.5. 单继承
    • 11.6. 子类重写属性和方法
    • 11.7. 多继承
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档