函数
函数定义
函数可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。在之前学习过程中已经使用过Python提供的许多内建函数,比如print()。函数不仅可以使用官方的,还可以自己创建函数,这中由我们自己编写的函数叫做用户自定义函数。
函数的创建
1、函数的创建使用关键字def
,后面跟上函数名
再在函数名后面加上一个括号并打上英文中的:
。
2、完成第一步之后,回车会有缩进,在这缩进下的语句即属于定义的函数,保持同样缩进。
3、在定义函数之后,有时需要传入参数,而这些参数必须写在括号里面。没有参数时,括号也是不能省
4、return 表达式]
用于结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None
示例代码:
def 函数名():
函数内容
return 返回的内容
设计函数最为重要的原则,单一职责原则(一个函数只做好一件事情)—>高度内聚
写程序的终极原则:高内聚(只做好一件事、只做自己的)、低耦合—>high cohesion lowcoupling
函数的作用
可以将一段重复的代码使用函数写,在之后我们需要这段重复代码时,我们只需要根据自己的要求更改一下所传入的参数即可,不必浪费时间在进行编写一段重复的代码。这一过程也即是函数的调用
编写函数并进行调用
# 定义一个名字叫sum_num的函数进行两数相加,a, b是传入的参数
def sum_num(a, b):
return a + b # 使用return 返回函数的结果
# 调用函数
if __name__ == '__main__':
sum = sum_num(2,3) # 将函数的返回值给sum
print(sum) # 输出sum的值 为5
if __name__ == '__main__':
这可以使得我们在其下面的代码只在本文件下进行使用,如果有其他的文件在调用这个文件的函数时,不必执行下面的代码。以后的进行函数的调用的时候,我们都会在下面编写。
函数的参数
函数中没有return
,那么函数默认返回None
。函数允许没有参数,同时允许参数有默认值。
# 定义一个名字叫sum_num的函数进行两数相加,a, b是传入的参数
def sum_num(a=2, b=3): # 给函数的参数a,b添加默认值,a=2,b=3
return a + b # 使用return 返回函数的结果
# 调用函数
if __name__ == '__main__':
sum = sum_num() # 调用时,不传入参数,则函数默认使用a,b的默认值2,3
print(sum) # 5
sum = sum_num(3,4) # 给函数的参数传入了值,函数的参数就会使用新值,不再使用默认值
print(sum) # 7
参数的各种使用:
def sum_num(a=0, b=0, c=0):
return a+b+c
# 参数的个数改变,顺序的变换
# 不传入参数,使用默认值进行相加
print(sum_num()) # 0
# 传入一个参数,函数会按定义函数时传入参数的顺序进行赋值,a,b,c; a=1,b=0,c=0
print(sum_num(1)) # 1
# 传入两个参数,按顺序赋值,剩下的使用默认值
print(sum_num(1,2)) # 3
# 传入三个参数,分别赋值给a,b,c
print(sum_num(1,2,3)) # 6
# 传入的参数可以不按顺序,但是需要用"参数名=参数值"的形式传入。
print(sum_num(b = 3,c =5,a = 6)) # 14
带默认值的参数必须放在不带默认值的参数之后
可变参数
在函数定义时,我们可以不指定参数个数,在调用时,根据需要传入参数。实现的方法是在函数括号里面加上*args
,args只是一个名字,可以改成其他的。
# 定义一个累加的函数,不确定传入的参数个数,使用`*`定义一个可变参数。
def total(*args):
total_num = 0
for value in args:
total_num += value
return total_num
# 调用函数,传入不同的参数
print(total(1,2)) # 3
print(total(1,2,3)) # 6
python标准库中的常用模块和函数
函数 | 说明 |
---|---|
abs | 返回一个数的绝对值,例如:abs(-1.3) 会返回1.3 。 |
bin | 把一个整数转换成以'0b' 开头的二进制字符串,例如:bin(123) 会返回'0b1111011' 。 |
chr | 将Unicode编码转换成对应的字符,例如:chr(8364) 会返回'€' 。 |
hex | 将一个整数转换成以'0x' 开头的十六进制字符串,例如:hex(123) 会返回'0x7b' 。 |
input | 从输入中读取一行,返回读到的字符串。 |
len | 获取字符串、列表等的长度。 |
max | 返回多个参数或一个可迭代对象(后面会讲)中的最大值,例如:max(12, 95, 37) 会返回95 。 |
min | 返回多个参数或一个可迭代对象(后面会讲)中的最小值,例如:min(12, 95, 37) 会返回12 。 |
oct | 把一个整数转换成以'0o' 开头的八进制字符串,例如:oct(123) 会返回'0o173' 。 |
open | 打开一个文件并返回文件对象(后面会讲)。 |
ord | 将字符转换成对应的Unicode编码,例如:ord('€') 会返回8364 。 |
pow | 求幂运算,例如:pow(2, 3) 会返回8 ;pow(2, 0.5) 会返回1.4142135623730951 。 |
print | 打印输出。 |
range | 构造一个范围序列,例如:range(100) 会产生0 到99 的整数序列。 |
round | 按照指定的精度对数值进行四舍五入,例如:round(1.23456, 4) 会返回1.2346 。 |
sum | 对一个序列中的项从左到右进行求和运算,例如:sum(range(1, 101)) 会返回5050 。 |
type | 返回对象的类型,例如:type(10) 会返回int ;而type('hello') 会返回str 。 |
函数是功能相对独立且会重复使用的代码的封装
高阶函数
Python中的函数是一等函数(一等公民):
1.函数可以作为函数的参数
2.函数可以作为函数的返回值
3.函数可以赋值给变量
如果把函数作为函数的参数或者返回值,这种玩法叫高阶函数。
# 定义一个计算函数,并且调用一个相加函数对传入的值进行相加
def calculate(init_value, func, *args, **kwargs): # **args表示传入的参数必须是键值对形式。
total = init_value
for arg in args: # 判断传入的参数是否属于整型或者浮点型,不然不能进行相应操作。
if type(arg) in (int, float):
total = func(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = func(total, value)
return total
def add(x, y):
return x + y
if __name__ == '__main__':
# 在calculate函数中出入add函数进行参数的相加
print(calculate(0, add, 11, 22, a = 33, b=44)) # 110 在函数调用函数时,括号不加
lambda函数:没有名字,一句话就能写完的函数,唯一的表达式就是返回值。用于创建的匿名函数
# 使用lambda函数创建一个匿名函数,直接用于函数的直接调用,不需要在使用def进行函数的创建
print(calculate(0, lambda x,y:x+y, 11, 22, a = 33, b=44)) # 110
所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
1、lambda 只是一个表达式,函数体比 def 简单很多。
2、lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
3、lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
4、虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
递归
函数调用自己----->递归:效率低下,有大量重复计算
函数入过直接或者间接调用自己,叫做递归调用
不管函数是调用别的函数,还是调用自身,一定要做到快速收敛。
在比较有限的调用次数内能够结束,而不是无限制的调用函数。
如果一个函数(通常指递归调用的函数)不能够快速收敛,那么就会产生以下错误:
RecursionError: maximum recursion depth exceeded
要点
1、递归体
2、收敛条件(递归出口)
应用(求阶乘)(汉诺塔)
def fac(num):
"""
求阶乘(递归)
:param num: 数
:return: 阶乘
"""
if num == 0: # 递归出口,没有则代码将会进行死循环
return 1
else:
# 内部调用过程,依此向下调用,再将返回值向上返回
# 向下调用,然后向上返回结果
# return 5 * fac(4) 5*24
# return 4 * fac(3) 4*6
# return 3 * fac(2) 3*2
# return 2 * fac(1) 2*1
# return 1 * fac(0) 1*1
# return 1
return num * fac(num - 1) # 120
if __name__ == '__main__':
pritn(fac(5)) # 120
# 汉诺塔 思想:将最大的上面的移动到中间柱子,把最大的放在第三根柱子,再把中间柱子上的移动到第三根。
def hanoi(num, a, b, c):
if num == 1:
print(a, '---->', c)
return
else:
hanoi(num - 1, a, c, b)
hanoi(1, a, b, c)
hanoi(num-1, b, a, c)
if __name__ == '__main__':
hanoi(3,'A','B','C')
# 输出结果
A ----> C
A ----> B
C ----> B
A ----> C
B ----> A
B ----> C
A ----> C
面向对象
Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。
类(Class):
用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
方法:
类中定义的函数。
类变量:
类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
方法重写:
如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
局部变量:
定义在方法中的变量,只作用于当前实例的类。
实例变量:
在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
继承:
即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
实例化:
创建一个类的实例,类的具体对象。
对象:
通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
类的定义
类使用class
定义一个类
class classname:
类块
在类的代码块中,我们需要写一些函数,我们说过类是一个抽象概念,那么这些函数就是我们对一类对象共同的动态特征的提取。写在类里面的函数我们通常称之为方法,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是self
,它代表了接收这个消息的对象本身。我们可以使用self.属性或者方法
访问类中的属性,方法等。
# 定义一个student类
class Student:
def __init__(self,no,name,sex): # 初始化类的属性,no(学号),name(姓名),sex(性别)
self.no = no
self.name = name
self.sex = sex
def study(self,course_name): # 类的方法:学习,并且传入一个参数学习的课程
"""学习"""
print(f'{self.name}正在学习{course_name}')
在定义好类后,尝试调用它。
if __name__ == '__main__':
stu1 = Student(123,'王大锤','男') # 实例化一个学生对象stu1,传入需要的参数
print(stu1.no) # 访问类中的学号,也可以访问其他属性
stu1.study('python') # 调用类中的study方法
# 输出
123
王大锤正在学习python
魔术方法
魔术方法:–>以__''__
形式,有特殊意义和用途的方法
__init__
---->初始化方法,在调用构造器语法创建对象是会被自动调用
__str__
----->获得对象的字符串表示,在调用print函数输出对象时会被自动调用获得
__repr__
---->对象的字符串表示,把对象放到容器中,用print输出容器时会自动调用
__lt__
------->原本是不允许比较大小,将运算符<赋给这个类中,是里面的数据可以进行小于比较
继承、多态(父类中的方法被子类重写)
继承定义:class 类名(被继承的类名):
子类可以调用父类的方法,也可以访问父类的属性。
多态:子类对父类已有的方法,重新给出自己的实现版本,这个过程叫做方法重写(override)
多态:在重写方法的过程中,不同的子类可以对父类的同一方法给出不同的实现版本,那么该方法在运行时就会表现多态性,给不同的对象发出同样的消息,不同的对象执行了不同的行为
两个类之间的可能关系:
is-a 关系: 继承---->从一个类派生出另一个类
class 类名(继承的类名) class Student(person)
has-a 关系: 关联 -----> 把一个类的对象作为另一个类的对象的属性
普通关联
强关联:整体和部分的关联,聚合和合成
use-a 关系: 依赖---->一个类的对象作为另一个类的参数或返回值
python中允许多重继承,一个类有一个或多个父类
尽量使用单一继承
被继承的叫父类。继承者叫子类
实例 :工资(月薪)结算系统
-部门经理:固定月薪,15000元
-程序员:计时结算月薪,每小时200元
-销售员:底薪+提成,底薪1800元,销售额5%提成
录入员工信息,自动结算月薪
from abc import abstractmethod
class Staff:
"""员工类"""
def __init__(self, name):
self.name = name
@abstractmethod # 代码实现不了,表示抽象的,需要导入库
def get_salary(self):
return
class DivisionManager(Staff): # 继承员工类
"""部门经理"""
def __init__(self, name):
super().__init__(name) # 父类中已经有了大家共同属性name,那么我们在子类中进行初始化属性时可以不用对共同属性进行初始化,可用该方法实现,多个共同属性,用逗号分隔。
def get_salay(self):
return 15000
class Programmer(Staff): # 继承员工类
"""程序员"""
def __init__(self, name, worktime_hour):
super().__init__(name)
self.worktime_hour = worktime_hour
def get_salary(self):
return 200 * self.worktime_hour
class Salesman(Staff): # 继承员工类
"""销售员"""
def __init__(self, name, sales_volume):
super().__init__(name)
self.sales_volume = sales_volume
def get_salay(self):
return 1800 + (0.05 * self.sales_volume)
if __name__ == '__main__':
manager = DivisionManager('王大锤')
print('部门经理的月薪:', manager.get_salay())
programmer = Programmer('张三', 250)
print('程序员的月薪:', programmer.get_salary())
salesman = Salesman('小王', 15000)
print('销售员的月薪:', salesman.get_salay())
三个不同职业的员工都继承了员工类,同时他们可以调用员工类中的属性,方法。
三个员工都对父类中的get_salay方法重写,分别实现了不同的计算薪资的方法,体现了该方法的多态性
面向对象编程的三大支柱:
抽象:提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)
封装(encapsulation):把数据和操作数据的函数从逻辑上组装成一个整体(对象)。隐藏实现细节,暴露简单的调用接口。
继承(inheritance):扩展已有的类创建新类,实现对已有类的代码复用。
多态(polymorphism):给不同的对象发出同样的消息,不同的对象执行了不同的行为。