Python:函数与面向对象编程总结
Posted better meˇ:)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python:函数与面向对象编程总结相关的知识,希望对你有一定的参考价值。
文章目录
函数
函数的定义
函数就是对实现某一特定功能的代码的封装。
在Python中可以使用def
关键字来定义函数,函数的命名规则跟变量的命名规则是一致的。
# 定义函数:def是定义函数的关键字、fac是函数名,num是参数(自变量)
def fac(num):
"""求阶乘"""
result = 1
for n in range(1, num + 1):
result *= n
# 返回num的阶乘(因变量)
return result
函数的参数
- 位置参数:传入参数的时候对号入座即可;也可以通过
参数名=参数值
的方式传入函数所需的参数,因为指定了参数名,传入参数的顺序可以进行调整。 - 命名关键字参数:在函数的参数列表中,写在
*
之后的参数,传参时,只能以参数名=参数值
的方式传参。 - 关键字参数:Python中可以通过
*kwargs
表达式语法传入0个或任意多个参数名=参数值
形式的参数,关键字参数会将传入的带参数名的参数组装成一个字典,参数名就是字典中键值对的键,而参数值就是字典中键值对的值。 - 可变参数:可以实现向一个函数中传0个或任意多个参数,Python中可以通过
*args
表达式语法来支持可变参数。(不能接收带参数名的参数)
注意:不带参数名的参数(位置参数)必须出现在带参数名的参数(关键字参数)之前
def add(*args, **kwargs):
"""加法"""
total = 0
for arg in args:
if type(arg) in (int, float):
total += arg
for value in kwargs.values():
if type(value) in (int, float):
total += value
return total
# 传入0个参数
print(add())
# 只传入可变参数
print(add(1, 2, 3))
# 只传入关键字参数
print(add(a=1, b=2, c=3))
# 传入位置参数和关键字参数,关键字参数在*args的‘*’之后,因此也是命名关键字参数
print(add(1, 2, c=3, d=4))
用模块管理函数
Python中每个文件就代表了一个模块(module),在不同模块可以有同名的函数,在使用函数时,通过import
关键字导入指定的模块,再使用完全限定名的调用方式就可以区分是哪个模块中的函数。
高阶函数
函数的参数和返回值可以是任意类型的对象,这就意味着函数本身也可以作为函数的参数或返回值,这就是所谓的高阶函数。
# 中间两个参数是命名关键字参数,必须带上参数名
def calculate(*args, init_value, op, **kwargs):
"""可以做任意二元运算的函数
:param init_value:初始值
:param op: 一个实现二元运算(+、*)的函数
:param args: 位置参数
:param kwargs: 关键字参数
:return: 运算结果
"""
total = init_value
for arg in args:
if type(arg) in (int, float):
total = op(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = op(total, value)
return total
def add(a, b):
return a + b
def mul(a, b):
return a * b
print(calculate(1, 2, 30, init_value=0, op=add))
print(calculate(1, 2, 3, init_value=0, op=mul))
print(calculate(15, 20, init_value=100, op=add, c=5))
Lambda函数
在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,那么我们可以使用Lambda函数来表示。如上面的add
函数和mul
函数
# 中间两个参数是命名关键字参数,必须带上参数名
def calculate(*args, init_value, op, **kwargs):
"""可以做任意二元运算的函数
:param init_value:初始值
:param op: 一个实现二元运算(+、-、*、/)的函数
:param args: 位置参数
:param kwargs: 关键字参数
:return: 运算结果
"""
total = init_value
for arg in args:
if type(arg) in (int, float):
total = op(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = op(total, value)
return total
# 调用calculate函数,通过lambda函数给op参数赋值
print(calculate(1, 2, 30, init_value=0, op=lambda x, y: x + y, a=5))
print(calculate(1, 2, 3, init_value=1, op=lambda x, y: x * y))
print(calculate(15, 20, init_value=100, op=lambda x, y: x - y))
递归调用
Python中允许函数嵌套定义,也允许函数之间相互调用,而且一个函数还可以直接或间接的调用自身。
递归函数的要点:
1.递归公式:(第n次与第n-1次的关系)
2.收敛条件(什么时候停止递归调用)
例题
例题1:汉诺塔问题
a柱上有n个盘子,要把所有盘子搬到b柱上,借用c柱帮助搬运,大盘不能压在小盘上面。
def move(n, a, c, b):
if n == 1:
print(f'{a}->{b}')
else:
move(n - 1, a, b, c)
move(1, a, c, b)
move(n - 1, c, a, b)
if __name__ == '__main__':
n = int(input('请输入盘子数:'))
move(n, 'a', 'c', 'b')
例题2:爬楼梯
有一个小孩爬楼梯,一次可以爬1个台阶、2个台阶或3个台阶,问爬完10个台阶,有多少种走法?
def step(n):
if n == 1:
return 1
elif n == 2:
return 2
elif n == 3:
return 4
else:
return step(n - 1) + step(n - 2) + step(n - 3)
if __name__ == '__main__':
print(step(10))
例题3:冒泡排序
def bubble_sort(nums, ascending=True, gt=lambda x, y: x > y):
"""冒泡排序
:param nums: 待排序的列表
:param ascending: 是否使用升序
:param gt: 比较两个元素大小的函数
:return: 排序后的列表
"""
sort_nums = nums[:]
for i in range(1, len(sort_nums)):
swapped = False
for j in range(0, len(sort_nums) - i):
if gt(sort_nums[j], sort_nums[j + 1]):
sort_nums[j + 1], sort_nums[j] = sort_nums[j], sort_nums[j + 1]
swapped = True
if not swapped:
break
if not ascending:
return sort_nums[::-1]
return sort_nums
nums = [15, 20, 10, 15, 12, 30, 65]
# 使用默认的gt函数
print(bubble_sort(nums, False))
print(nums)
words = ['apple', 'watermelon', 'hello', 'zoo', 'internationalization']
# 修改gt函数根据字符串长度排序
print(bubble_sort(words, gt=lambda x, y: len(x) > len(y), ascending=False))
例题4:编写实现查找元素的函数
若列表元素无序,使用顺序查找;若列表元素有序,使用二分查找(折半查找)。
def seq_search(items, key):
for index, i in enumerate(items):
if i == key:
return index
return -1
def bin_search(items, key, cmp=lambda x, y: x - y):
"""折半查找(渐进时间复杂度:(log2 n))
:param cmp: 比较两个元素的大小
:param items: 一个有序列表
:param key: 查找的元素
:return: 找到了返回元素下标,没找到返回-1
"""
high = len(items) - 1
low = 0
while low <= high:
mid = (high + low) // 2
if cmp(items[mid], key) > 0:
high = mid - 1
elif cmp(items[mid], key) < 0:
low = mid + 1
else:
return mid
return -1
if __name__ == '__main__':
nums1 = [78, 58, 93, 42, 45, 66, 12, 15]
nums2 = [1, 15, 25, 40, 80, 92, 98, 99]
print(seq_search(nums1, 58))
print(seq_search(nums1, 40))
print(bin_search(nums2, 99))
print(bin_search(nums2, 85))
例题5:判断快乐数
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程,如果数字变成了1,这个数就是快乐数,如果无限循环始终变不到1,这个数就不是快乐数。
def is_happy_num(num):
temp = []
while num not in temp:
temp.append(num)
total = 0
while num != 0:
total += (num % 10) ** 2
num //= 10
if total == 1:
return True
num = total
return False
for i in range(100, 1001):
if is_happy_num(i):
print(i, end=' ')
面向对象编程
面向对象编程:把一组数据和处理数据的方法组成对象,把行为相同的对象归纳为类,通过封装隐藏对象的内部细节,通过继承实现类的特化与泛化,通过多态实现基于对象类型的动态分派。
面向对象编程的过程
-
定义类
—数据抽象:找到对象相关的静态特征(属性)—>找名词
—行为抽象:找到和对象相关的动态特征(方法)—>找动词
-
创建对象
-
给对象发消息
定义类
在Python中,可以使用class
关键字加上类名来定义类,通过缩进我们可以确定类的代码块,就如同定义函数那样。
class Student:
def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age
def study(self, course_name):
print(f'{self.name}正在学习{course_name}.')
def play(self, game_name):
print(f'{self.name}正在玩{game_name}游戏.')
创建对象
stu1 = Student('小青', 20)
stu2 = Student('小白', 22)
给对象发消息
# 通过“类.方法”调用方法,第一个参数是接收消息的对象,第二个参数是学习的课程名称
Student.study(stu1, 'Python程序设计')
# 通过“对象.方法”调用方法,点前面的对象就是接收消息的对象,只需要传入第二个参数
stu1.study('Python程序设计')
stu2.play('王者荣耀')
一般使用“对象.方法”调用方法
面向对象的支柱
面向对象编程的四大支柱:
- 抽象:提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
- 封装:把数据和操作数据的函数从逻辑上组成一个整体(对象)。隐藏实现细节,暴露简单的调用接口。
- 继承:扩展已有的类创建新类,实现对已有类的代码复用。
- 多态:给不同的对象发出同样的消息,不同的对象执行了不同的行为。
静态方法和类方法
使用staticmethod
装饰器声明某个方法是某个类的静态方法,如果要声明类方法,可以使用classmethod
装饰器。可以直接使用类名.方法名
的方式来调用静态方法和类方法,二者的区别在于,类方法的第一个参数是类对象本身,而静态方法则没有这个参数。简单的总结一下,对象方法、类方法、静态方法都可以通过类名.方法名
的方式来调用,区别在于方法的第一个参数到底是普通对象还是类对象,还是没有接受消息的对象。
继承和多态
继承的语法是在定义类的时候,在类名后的圆括号中指定当前类的父类。在子类的初始化方法中,我们可以通过super().__init__()
来调用父类初始化方法,super
函数是Python内置函数中专门为获取当前对象的父类对象而设计的。子类除了可以通过继承得到父类提供的属性和方法外,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力。
子类继承父类的方法后,还可以对方法进行重写(重新实现该方法)。
多态就是调用相同的方法,不同的子类对象做不同的事情。
魔术方法
魔术方法(魔法方法):有特殊用途和意义的方法。
Python中有很多魔法方法,下面是几个常用的:
__init__
—> 初始化方法,在调用构造器语法创建对象的时候会被自动调用
__str__
—> 获得对象的字符串表示,在调用print函数输出对象时会被自动调用
__repr__
—> 获得对象的字符串表示,把对象放到容器中调用print输出时会自动调用
__lt__
—> 在使用 < 运算符比较两个对象大小时会自动调用
类和类之间的关系
类和类之间的关系可以粗略的分为is-a关系(继承)、has-a关系(关联)和use-a关系(依赖)。
- is-a关系:继承
- has-a关系:关联 —>把一个类的对象作为另一个类的对象的属性
—普通关联
—强关联:整体和部分的关联,聚合和合成 - use-a关系:依赖 —> 一个类的对象作为另一个类的方法的参数或返回值
以上是关于Python:函数与面向对象编程总结的主要内容,如果未能解决你的问题,请参考以下文章