函数
- 函数的作用:世界级的编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”我们可以将重复代码封装到一个称为“函数”的代码块中,在需要的地方,我们只需要“调用函数”就可以了。
- 定义函数:在Python中可以使用
def
关键字来定义函数,命名规则跟变量的命名规则是一致的,在函数名后面的圆括号中可以放置传递给函数的参数,函数执行完成后我们会通过return
关键字来返回函数的执行结果,就是函数的因变量。 - 我们可以通过函数对代码进行重构。所谓重构,是在不影响代码执行结果的前提下对代码的结构进行调整。
函数的参数
参数的默认值:
- 果函数中没有
return
语句,那么函数默认返回代表空值的None
。另外,在定义函数时,函数也可以没有自变量,但是函数名后面的圆括号是必须有的。Python中还允许函数的参数拥有默认值。 - 带默认值的参数必须放在不带默认值的参数之后,否则将产生
SyntaxError
错误,错误消息是:non-default argument follows default argument
,翻译成中文的意思是“没有默认值的参数放在了带默认值的参数后面”。
可变参数
- 有的时候我们并不知道函数的调用者会向该函数传入多少个参数,这个时候可变参数就可以派上用场。
- *args —> 可变参数 —> 可以接收零个或任意多个位置参数 —> 将所有的位置参数打包成一个元组
- **kwargs —> 可以接收零个或任意多个关键字参数 —> 将所有的关键字参数打包成一个字典。
导入函数
- 使用函数的时候我们通过
import
关键字导入指定的模块再使用完全限定名的调用方式。 - 在导入模块时,还可以使用
as
关键字对模块进行别名,这样我们可以使用更为简短的完全限定名。 - 我们也可以使用
from...import...
语法从模块中直接导入需要使用的函数。
高阶函数
python中的函数是一等函数(一等公民):
- 函数可以作为函数的参数
- 函数可以作为函数的返回值
- 函数可以赋值给变量
如果把函数作为函数的参数或者返回值,这种玩法通常称之为高阶函数。
通常使用高阶函数可以实现对原有函数的解耦合操作。
Lambda函数 —> 没有名字而且一句话就能写完的函数,唯一的表达式就是函数的返回值。
函数可以自己调用自己吗?
- 函数如果直接或间接的调用了自身,这种调用称为递归调用。
- 不管函数是调用别的函数,还是调用自身,一定要做到快速收敛。
在比较有限的调用次数内能够结束,而不是无限制的调用函数。 - 如果一个函数(通常指递归调用的函数)不能够快速收敛,那么就很有可能产生下面的错误RecursionError: maximum recursion depth exceeded最终导致程序的崩溃。
- 递归函数的两个要点:
递归公式(第n次跟第n-1次的关系)
收敛条件(什么时候停止递归调用)
函数的应用
例子1
设计一个生成指定长度验证码的函数
mport random
import string
def get_captcha_code(length: int = 4) -> str:
"""生成随机验证码
:param length: 验证码的长度
:return: 随机验证码字符串
"""
selected_chars = random.choices(string.digits + string.ascii_letters, k=length)
return ''.join(selected_chars)
for _ in range(10):
print(get_captcha_code())
例子2
写实现对列表元素进行冒泡排序的函数,设计函数的时候,一定要注意函数的无副作用性(调用函数不影响调用者)
def bubble_sort(items, *, ascending=True, gt=lambda x, y: x > y):
"""冒泡排序
:param items: 待排序的列表
:param ascending: 是否使用升序
:param gt: 比较两个元素大小的函数
:return: 排序后的列表
"""
items = items[:]
for i in range(1, len(items)):
swapped = False
for j in range(0, len(items) - i):
if gt(items[j], items[j + 1]):
items[j], items[j + 1] = items[j + 1], items[j]
swapped = True
if not swapped:
break
if not ascending:
items = items[::-1]
return items
if __name__ == '__main__':
nums = [35, 96, 12, 78, 56, 64, 39, 80]
print(bubble_sort(nums, ascending=False))
# print(nums)
words = ['apple', 'watermelon', 'hello', 'zoo', 'internationalization']
print(bubble_sort(words, gt=lambda x, y: len(x) > len(y), ascending=False))
思维导图
面向对象编程入门
-
指令式编程 —> 面向过程(函数)编程 —> 程序比较简单的时候没有任何毛病
-
编程范式(程序设计的方法论):面向对象编程 / 函数式编程
-
对象:对象是可以接收消息的实体,面向对象编程就是通过给对象发消息达到解决问题的目标。
-
对象 = 数据 + 函数(方法)—> 对象将数据和操作数据的函数从逻辑上变成了一个整体。
~ 一切皆为对象
~ 对象都有属性和行为
~ 每个对象都是独一无二的
~ 对象一定属于某个类 -
类:将有共同特征(静态特征和动态特征)的对象的共同特征抽取出来之后得到的一个抽象概念。简单的说,类是对象的蓝图(模板),有了类才能够创建出这种类型的对象。
面向对象编程:
-
-
定义类 —>
- 数据抽象:找到和对象相关的静态特征(属性)—> 找名词
- 行为抽象:找到和对象相关的动态特征(方法)—> 找动词
class
关键字加上类名来定义类, 类的命名使用驼峰命名法(每个单词首字母大写) - 造对象
- 发消息
魔术方法(魔法方法)—> 有特殊用途和意义的方法
~ __init__ ---> 初始化方法,在调用构造器语法创建对象的时候会被自动调用
~ __str__ ---> 获得对象的字符串表示,在调用print函数输出对象时会被自动调用
~ __repr__ ---> 获得对象的字符串表示,把对象放到容器中调用print输出时会自动调用
---> representation
~ __lt__ ---> 在使用 < 运算符比较两个对象大小时会自动调用
静态方法和类方法
- 我们在类里面写的函数,通常称之为方法,它们基本上都是发给对象的消息。但是有的时候,我们的消息并不想发给对象,而是希望发给这个类(类本身也是一个对象),这个时候,我们可以使用静态方法或类方法。
静态方法 - 发给类的消息 —> @staticmethod —> 装饰器
类方法 - 发给类的消息 —> @classmethod —> 装饰器 —> 第一个参数(cls)是接收消息的类 - 二者的区别在于,类方法的第一个参数是类对象本身,而静态方法则没有这个参数。简单的总结一下,对象方法、类方法、静态方法都可以通过
类名.方法名
的方式来调用,区别在于方法的第一个参数到底是普通对象还是类对象,还是没有接受消息的对象。静态方法通常也可以直接写成一个独立的函数,因为它并没有跟特定的对象绑定。
继承和多态
- 如果一个变量的取值只有有限个选项,可以考虑使用枚举类型。
Python中没有定义枚举类型的语法,但是可以通过继承Enum类来实现枚举类型。
结论1:枚举类型是定义符号常量的最佳选择!!!
结论2:符号常量(有意义的名字)总是优于字面常量!!! - 继承:对已有的类进行扩展创建出新的类,这个过程就叫继承。
提供继承信息的类叫做父类(超类、基类),得到继承信息的类称为子类(派生类)。
注意:继承是实现代码复用的一种手段,但是千万不要滥用继承。 - 子类直接从父类继承公共的属性和行为,再添加自己特有的属性和行为,所以子类一定是比父类更强大的,任何时候都可以用子类对象去替代父类对象。
注意:Python中的继承允许多重继承,一个类可以有一个或多个父类。如果不是必须使用多重继承的场景下,请尽量使用单一继承。
两个类之间有哪些可能的关系???
- ~ is-a关系:继承 —> 从一个类派生出另一个类。
a student is a person.
a teacher is a person. - ~ has-a关系:关联 —> 把一个类的对象作为另外一个类的对象的属性。
a person has an identity card.
a car has an engine.- (普通)关联
- 强关联:整体和部分的关联,聚合和合成
- ~ use-a关系:依赖 —> 一个类的对象作为另外一个类的方法的参数或返回值。
a person use a vehicle.
面向对象编程的四大支柱:
- 抽象(abstraction):提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
- 封装(encapsulation):把数据和操作数据的函数从逻辑上组装成一个整体(对象)。
隐藏实现细节,暴露简单的调用接口。 - 继承(inheritance):扩展已有的类创建新类,实现对已有类的代码复用。
- 多态(polymorphism):给不同的对象发出同样的消息,不同的对象执行了不同的行为。
注意方法重写:子类对父类已有的方法,重新给出自己的实现版本,在重写方法的过程中,不同的子类可以对父类的同一个方法给出不同的实现版本,那么该方法在运行时就会表现出多态行为
思维导图
应用
例子1
工资(月薪)结算系统
三类员工:
~ 部门经理:固定月薪,15000元
~ 程序员:计时结算月薪,每小时200元
~ 销售员:底薪+提成,底薪1800元,销售额5%提成
录入员工信息,自动结算月薪
from abc import abstractmethod
class Employee:
def __init__(self, no, name):
self.no = no
self.name = name
@abstractmethod
def get_salary(self):
pass
class Manager(Employee):
def get_salary(self):
return 15000
class Programmer(Employee):
def __init__(self, no, name):
super().__init__(no, name)
self.working_hour = 0
def get_salary(self):
return 200 * self.working_hour
class Salesman(Employee):
def __init__(self, no, name):
super().__init__(no, name)
self.sales = 0
def get_salary(self):
return 1800 + 0.05 * self.sales
def main():
emps = [
Manager(1122, '刘备'), Programmer(2233, '诸葛亮'),
Salesman(3344, '关羽'), Salesman(4455, '张飞'),
Programmer(5566, '庞统'), Salesman(6677, '马超')
]
for emp in emps:
if type(emp) == Programmer:
emp.working_hour = int(input(f'请输入{emp.name}本月工作时长: '))
elif type(emp) == Salesman:
emp.sales = float(input(f'请输入{emp.name}本月销售额: '))
print(f'{emp.name}本月工资: {emp.get_salary()}元')
if __name__ == '__main__':
main()
例子2
-
扑克游戏,四个玩家参与,先洗牌,再把牌发到四个玩家的手上。
-
牌(Card)
- 属性:花色(suite)、点数(face)
- 行为:显示
-
扑克(Poker)
- 属性:保存牌的列表
- 行为:洗牌(shuffle)、发牌(deal)
-
玩家
- 属性:名字(昵称)、保存玩家手牌的列表
- 行为:摸牌(get)、整理(arrange)
import random
class Card:
"""牌"""
def __init__(self, suite, face):
"""
初始化方法
:param suite:花色
:param face: 点数
"""
self.suite = suite
self.face = face
def __str__(self):
return self.show()
def __repr__(self):
return self.show()
def __lt__(self, other):
# if self.suite == other.suite:
return self.face < other.face
# return ord(self.suite)<ord(other.suite)
def show(self):
suites = {'S': '♠', 'H': '♥', 'C': '♣','D':'♦'}
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
return f'{suites[self.suite]}{faces[self.face]}'
class Poker:
"""扑克"""
def __init__(self):
self.cards = [Card(suite, face)
for suite in 'SHCD'
for face in range(1, 14)]
self.counter = 0
def shuffles(self):
"""洗牌"""
self.counter = 0
random.shuffle(self.cards)
def deal(self):
"""发牌"""
card = self.cards[self.counter]
self.counter += 1
return card
def has_more(self):
"""是否还有牌"""
return self.counter<len(self.cards)
class Player:
def __init__(self, nickname):
self.nickname = nickname
self.cards = []
def get_one_card(self,card):
"""摸牌"""
self.cards.append(card)
return self.cards
def arrange(self):
"""礼牌"""
self.cards.sort()
def show(self):
print(self.nickname,end=':')
for card in self.cards:
print(card,end=' ')
print()
def main():
poker = Poker()
poker.shuffles()
nicknames = ['东邪','西毒','北乔峰','南慕容']
players = [Player(nickname) for nickname in nicknames ]
for _ in range(13):
for player in players:
card = poker.deal()
player.get_one_card(card)
for player in players:
player.arrange()
player.show()
if __name__ == '__main__':
main()