字符串常用API之join
# 列表转换为字符串
list = ['a', 'b', 'c']
str = ' '.join(list) # a b c
字符串常用API之split
# 数据切分操作
str = 'a b c'
list = str.split(' ') # ['a', 'b', 'c']
字符串常用API之replace
# 替换字符串
str = 'a b c'
str1 = str.replace(' ', '|') # a|b|c
字符串常用API之strip
# 去掉首尾的空格
str = ' a b c '
str1 = str.strip() # a b c
列表推导式
# 单分支if
list1 = [i for i in range(4) if i%2==0] # [0, 2]
# 双分支if else
list2 = [i if i%2==0 else 'odd' for i in range(4)] # [0, 'odd', 2, 'odd']
字典推导式
dc = {'a': 1, 'b': 2, 'c': 3}
d_new = {k : v ** 2 for k,v in dc.items() if v > 1 } # {'a': 1, 'b': 4, 'c': 9}
# items()方法得到的是字典的键值对元组
定义函数的注意事项
- pycharm 自动格式化快捷键:
ctrl+alt+L
- 定义空函数
- 使用
pass
语句占位 - 写函数注释 comments
- 使用
为参数设置默认值
- 定义函数时可以指定形式参数的默认值
- 指定默认值的形式参数必须放在所有参数的最后,否则会产生语法错误
-
param=default_value
:可选,指定参数并且为该参数设置默认值为 default_value
def function_name(..., [param=default_value]):
[function_body]
可变参数
- 可变参数也称为不定长参数
- 传入函数中实际参数可以是任意多个
- 常见形式
*args
**kwargs
*args
- 接收任意多个实际参数,并将其放到一个元组中
- 使用已经存在的列表或元组作为函数的可变参数,可以在列表的名称前加
*
def print_language(*args):
print(args)
print_language("python", "java", "php", "go")
params = ["python", "java", "php", "go"]
print_language(*params)
**kwargs
- 接收任意多个类似关键字参数一样显式赋值的实际参数,并将其放到一个字典中
- 使用已经存在字典作为函数的可变参数,可以在字典的名称前加
**
def print_info(**kwargs):
print(kwargs)
print_info(Tom=18, Jim=20, Lily=12)
params = {'Tom':18, 'Jim':20, 'Lily':12}
print_language(**params)
Lambda匿名函数
匿名函数定义:
- 没有名字的函数
- 用 lambda 表达式创建匿名函数
匿名函数使用场景
- 需要一个函数,但是又不想费神去命名这个函数
- 通常在这个函数只使用一次的场景下
- 可以指定短小的回调函数
语法
- result:调用 lambda 表达式
- [arg1 [, arg2, …. , argn]]:可选,指定要传递的参数列表
- expression:必选,指定一个实现具体功能的表达式
result = lambda [arg1 [, arg2, .... , argn]]: expression
lambda是一个表达式,并不是一个语句
lambda的主体是只有一行的简单表达式,并不能扩展成一个多行的代码块
lambda x: x * x
相当于
def f(x):
return x * x
也可以把返回值返回
f = lambda x: x * x
f(5)
相当于
def build(x, y):
return lambda: x * x + y * y
匿名函数实现列表排序
entity_dict = [{'name': '5', 'count': 1}, {'name': '6','count':4}, {'name': '2', 'count': 2},{'name': '5', 'count': 6}]
#按name排序
entity_dict_1 = sorted(entity_dict, key=lambda x: x['name'])
#按count降序排序
entity_dict_2 = sorted(entity_dict, key=lambda x: x['count'], reverse=True)
#name相同按count降序排序
entity_dict_3 = sorted(entity_dict, key=lambda x: (x['name'], -x['count']))
yi = {"C":3, "J":1, "B":2}
d = {k: v for k, v in sorted(yi.items(), key=lambda x: x[1])} # {'J': 1, 'B': 2, 'C': 3}
类和对象
类的定义
- 语法
class 类名(父类名):
"""类的帮助信息"""
属性
方法
类名我们一般采用大驼峰命名法,每个单词的首字母都需大写。
# class_def.py
# 类的声明
class Human(object):
"""人类"""
# 定义属性(类属性)
message = "这是类属性"
# 通过类访问类属性
print(Human.message)
类的方法
- 实例方法(最常用)
- 构造方法(通常用来进行实例化操作)
- 类方法
- 静态方法
构造方法与实例化
- 作用:实例化对象
- 语法:
def __init__(self, 参数列表)
—固定名称 - 访问:
类名(参数列表)
# constructor_method.py
class Human:
# 定义属性(类属性)
message = "这是类属性"
# 构造方法(用于构造并直接返回该类的对象)
def __init__(self, name, age):
# 实例变量(用self装饰的变量,也叫实例属性)
self.name = name
self.age = age
print("这是构造方法")
# 实例化对象
person = Human("哈利波特", 12)
# 通过实例访问类属性
print(person.message)
# 通过实例访问实例属性
print(person.name)
print(person.age)
实例方法
- 作用:提供每个类的实例共享的方法
- 语法:
def 方法名(self, 参数列表)
- 访问:
实例.方法名(参数列表)
# instance_method.py
class Human:
# 实例方法
def study(self, course):
print(f"正在学习{course}")
# 实例化
person = Human() # 由于类里面没有显示地声明__init__方法,因此python默认会提供一个无参的构造方法给我们
# 通过实例访问实例方法
person.study("python")
类方法
- 作用:可以操作类的详细信息
- 语法:
@classmethod
- 访问:
类名.类方法名(参数列表)
# class_method.py
class Human:
# 类属性
population = 0
# 类方法
@classmethod # 首先定义一个实例方法,然后用@classmethod装饰成一个类方法
def born(cls):
print("这是类方法")
cls.population += 1 # 通过cls.可以访问到自身的类 属性
# 通过类名访问类方法
Human.born()
print(Human.population)
注意:
- 必须使用@classmethod装饰器来声明这是个类方法,而不是一个普通的实例方法
- 类方法接收一个默认的参数,也是必填的参数cls,该参数指向类本身Human,其中最好写成cls,虽然写其他也不会报错
- 如果类方法中用到了实例变量,而你又想通过类直接访问类方法,必须要将实例变量变成类变量后,类方法才可以脱离示例被访问
静态方法
@staticmethod
静态方法不能访问类的数据,换句话说,他们不需要访问类的数据,他们没有class这样的关键字,可以自己独立工作,且他们不能设置类的状态和实例状态。
# static_method.py
class Human:
# 静态方法
@staticmethod # 使用@staticmethod将普通实例方法装饰为静态方法后,就不需要self默认参了,即不需要绑定到类class了
def grow_up():
print("这是静态方法")
# 通过类名访问静态方法
Human.grow_up() # 可以在不创建类的情况下直接使用类名访问到静态方法
静态方法是用于创建通用的工具函数,这样就可以把常用函数放在一个类中进行管理。
我们使用类方法和静态方法的时候都是通过类名直接访问的,省去实例化的步骤
作业
Python三大特性:封装、继承、多态
Python三大特性之封装(encap)
封装的概念:
- 隐藏:属性和实现细节,不允许外部直接访问
- 暴露:公开方法,实现对内部信息的擦欧总和访问
封装的作用
- 限制安全的访问和操作,提高数据安全性
- 可进行数据检查,从而有利于保证对象信息的完整性
封装的实现:隐藏
python本身没什么隐藏的方法,我们可以通过以下技巧来实现。
为了保证类的属性或者方法不被外部任意访问,可以在类的属性或者方法名前加"_“或”__"来限制访问权限
- 保护属性:_属性名
- 私有属性:__属性名
- 被视作 _类名_属性名
class Account:
# 普通属性
bank = "BOC"
# 保护属性(内部属性)
_username = "Hogwarts"
# 私有属性
__password = "888"
# 通过类名访问类属性
print(Account.bank) # 将会打印 BOC
print(Account._username) # 将会打印 Hogwarts
print(Account.__password) # 将会引发 AttributeError
print(Account.__dict__)
# 实例化
obj = Account()
# 实例访问类属性
print(obj.bank) # 将会打印 BOC
print(obj._username) # 将会打印 Hogwarts
print(obj.__username) # 将会引发AttributeError
打印_username的时候代码代码提示中没有这个属性,说明并不建议做此操作,但是强行使用会有波浪线,不报错也能输出------尽量不要在类外部使用
私有属性相当于给属性匿名隐藏了,外部使用会报错,因为根本找不到
__dict__可以看到类中真正具有哪些属性,因此实际上__password 属性已经被匿名为_Account__password
封装的实现:暴露
获取私有属性值-> getter
修改私有属性值->setter
- 提供数据访问功能(getter)(通过此方法可以在类外部访问私有属性)
- 通常称为计算属性
- 语法:使用@property装饰器
- 调用:实例.方法名
使用@property合成的属性叫计算属性,这种属性不真正存储任何的状态,它的值是通过某种算法计算得到的
class Account:
# 普通属性
bank = "BOC"
# 内部属性
_username = "Hogwarts"
# 私有属性
__password = "888"
@property
def password(self):
return self.__password
# 实例化对象
obj = Account()
# 访问实例的私有属性
print(obj.password) # 将会打印 888
解释一下:因为password必须要通过构造方法来访问,因此需要实例化对象后才能访问,而其他属性还可以通过类直接访问
- 提供数据操作功能(setter)
- 语法:使用@计算属性名.setter装饰器
- 调用:实例.方法名
class Account:
# 普通属性
bank = "BOC"
# 内部属性
_username = "Hogwarts"
# 私有属性
__password = "888"
@property
def password(self):
return self.__password
@password.setter
def password(self, value):
# 增加数据的校验
if len(value) >= 8:
self.__password = value
else:
print("密码长度最少要有8位!")
# 实例化对象
obj = Account()
# 修改私有属性(满足校验条件)
obj.password = "hogwarts666" # 修改成功
print(obj.password) # 将会打印 hogwarts666
# 修改私有属性(不满足校验条件)
obj.password = "123" # 修改不会生效
print(obj.password) # 将会打印 888
解释一下:必须要先能够访问私有属性才能修改私有属性,obj.password有赋值就是修改,没赋值就是属性本身的值
Python三大特性之继承
继承的概念
继承
- 复用父类的公开属性和方法
- 拓展出新的属性和方法
继承的实现(inheritant)
- 语法:class 类名(父类列表)
- 默认父类是object
- Python支持多继承
# inheritance_demo.py
class Human:
"""人类"""
# 类属性
message = "这是Human的类属性"
# 构造方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def live(self):
print("住在地球上")
class Student(Human):
"""学生类"""
def study(self):
print("正在学习")
# 实例化子类对象
stu = Student("哈利波特", 12)
# 访问类属性(继承)
print(stu.message)
# 访问实例属性(继承)
print(stu.name, stu.age)
# 访问实例方法(继承)
stu.live()
# 访问实例方法(扩展)
stu.study()
类型检查
- isinstance(实例名, 类名)
- 检查对象是否是某个类及其派生类的实例
- issubclass(类名1, 类名2)
- 检查类名1是否是类名2的子类
# relation_demo.py
# 人类
class Human:
pass
# 学生类
class Student(Human):
pass
# 老师类
class Teacher(Human):
pass
# 检查实例与类的关系
stu = Student()
print(isinstance(stu, Human)) # 将会打印 True
# 检查类与类的关系
print(issubclass(Student, Human)) # 将会打印 True
print(issubclass(Student, Teacher)) # 将会打印 False
Python三大特性之多态(Polymorphism)
多态的概念
同名方法呈现多种行为
运算符的多态表现
- +号
- 加法:数字+数字
- 拼接:字符串+字符串
- 合并:列表+列表
# 加法:数字+数字
print(1 + 1) # 打印 2
# 拼接:字符串+字符串
print("Hello" + "World") # 打印 Hello World
# 合并:列表+列表
print([1, 2] + [3]) # 打印 [1, 2, 3]
函数的多态表现
- len()函数
- 可以接收字符串
- 可以接收列表
# 参数是字符串
print(len("Hogwarts"))
# 参数是列表
print(len([1, 3, 5]))
函数的多态表现
- 同名变量调用同名方法呈现多种行为
(同名变量x调用同名方法speak)
# poly_method.py
class China:
def speak(self):
print("汉语")
class Usa:
def speak(self):
print("英语")
# 实例化对象
cn = China()
us = Usa()
for x in (cn, us):
# 同一个变量在调用同名方法时,呈现不同的行为
# 具体呈现哪种行为,由该变量所引用的对象决定
x.speak()
多态与继承
- 方法重写(Override):子类的方法名称与父类的相同
- 重写构造方法
- super().init()
- 父类名.init(self)
子类方法重写的原则:当子类实例访问父类的同名方法时会优先访问自己的方法
# override_demo.py
class Human:
"""人类"""
message = "这是Human的类属性"
# 构造方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def live(self):
print(f"住在地球上")
class Student(Human):
"""学生类"""
# 重写父类的构造方法
def __init__(self, name, age, school):
# 访问父类的构造方法
super().__init__(name, age)
# super(Student, self).__init__(name, age)
# Human.__init__(self, name, age)
# 子类实例属性(个性)
self.school = school
# 重写父类的实例方法
def live(self):
print(f"住在{self.school}")
# 实例化子类对象
stu = Student("哈利波特", 12, "Hogwarts")
# 访问实例方法
stu.live() # 将会打印 住在Hogwarts
Python中要求:如果子类重写了父类的构造方法,那么子类的构造方法必须调用父类的构造方法
子类构造方法调用父类构造方法的两种方式:
- super().init(name, age)
- Human.init(self, name, age)
如果在上面的子类构造方法中重写name和age属性也可以,打印出来将是子类重写后的内容
self.name = "lucy"
self.age = "20"
Python模块
Python的程序结构
- 组成
- package包
- module模块
- function方法
模块
- 包含Python定义和语句的文件
- .py文件
- 作为脚本运行
模块导入
- import 模块名
- from <模块名> import <方法/变量/类>
- from <模块名> import *(导入模块里的所有)
- 注意:
- 同一个模块写多次,只被导入一次
- import应该放在代码的顶端
- 注意:
模块分类
- 系统内置模块,如re,sys,json等,直接import就能导入
- 第三方的开源模块,如numpy,pandas等,在file->settings->Python Interpreter直接安装,pycharm能帮我们完成pip install的操作
- 自定义模块
注意:标准用法是每个模块分开导入,不要一次性导入;一个模块中的方法或类可以一次性导入
模块的调用
- 调用另一个模块的方法
# xfy.py
def xfy_method():
print("调用xfy_method方法!")
class XfyClass:
message = "调用XfyClass类!"
# demo.py
from xfy import xfy_method, XfyClass
# from xfy import *
# 调用方法
xfy_method() # 调用xfy_method方法!
# 调用类
print(XfyClass.message) # 调用XfyClass类!
常用方法
- dir()找出当前模块定义的对象(想知道当前模块有哪些对象可以调用)
- dir(sys)找出参数模块定义的对象
搜索路径
Python解释器对模块位置的搜索顺序是:
- 1、包含输入脚本的目录(如果未指定文件,则为当前目录)
- 2、PYTHONPATH(目录名称列表,语法与shell变量相同PATH)
- 3、安装的默认路径
注意:函数名可重复,但是避免与系统重复
Python错误与异常
错误
- 语法错误(语法:syntax)
- 逻辑错误
- 系统错误(内存泄漏等)
异常
- 程序执行过程中出现的未知错误
- 语法和逻辑都是正常的
- 程序业务逻辑不完善引起的程序漏洞(bug)
如两数相除,除数为0的情况
异常与错误
- 异常可以被捕获和处理
- 错误一般是编码错误,逻辑错误,系统错误
常见的异常类型
除零异常、名称异常、索引异常(下标越界)、键异常、值异常、属性异常等
异常捕获与异常处理
try:
"""执行代码"""
except:
"""发生异常时执行的代码"""
"""后面的可选"""
else:
"""没有异常时执行的代码"""
finally:
"""不管有没有异常都会执行的代码"""
finally可以用在文件的操作中,最后关闭文件f.close()的动作放在finally中。如果后面还想执行代码一定要放在finally中。
使用raise抛出异常:(对于开发人员会使用)
def set_age(num):
if num <= 0 or num > 200:
raise ValueError
else:
print(f"设置的年龄为: {num}")
set_age(-1) # ValueError
也可以在ValueError中加参数打印错误情况
def set_age(num):
if num <= 0 or num > 200:
raise ValueError(f"值错误: {num}")
else:
print(f"设置的年龄为: {num}")
set_age(-1) # ValueError
自定义异常
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
def set_age(num):
if num <= 0 or num > 200:
raise MyError(f"值错误: {num}")
else:
print(f"设置的年龄为: {num}")
set_age(-1) # __main__.MyError: '值错误: -1'
类型提示
想指定参数是string型,返回的值是string型,但是传入的是int,就会有提示信息,不需要去源码里分析
以及可以联想
类型别名
如果类型需要在别的地方用到
Vector = list[float] # 指定是列表,列表里的每一个元素都是浮点数型的
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
print(scale(2.0, [2.0, 3.0, 4.0])) # [4.0, 6.0, 8.0]
注意:类型提示并不具有检查功能,如果scale里传入的参数是字符串,也可以正常运行
设置提示类型
Settings->Edits->Inspections->Python->type check
自定义类型
class Student:
name: str
age: int
def __init__(self):
self.name = "lucy"
self.age = 12
def get_stu(name: str)->Student:
return Student()
print(get_stu("harry").name) # --> 有相应的提示
print(get_stu("harry").age) # --> 有相应的提示
静态代码检查功能
首先需要安装第三方工具mypy包
pip install mypy
mypy可以检查代码中的类型,前提是一定要添加类型提示
a : list[int] = []
a = [1, 2, 3]
a : list[int] = []
a = [1, 2, "1"]
pycharm中不能使用shell的解决方法
dataclass
dataclass是Python内置的模块
优势:
- 可读性强
- 操作灵活
- 轻量
应用场景: - 创建对象
- 完美融合平台开发ORM框架
案例
- 场景:如果创建一只猫,信息包括猫的名字、体重、颜色。同时打印这个对象的时候,希望能打印出一个字符串(包含猫的各种信息)应该如何编写代码
- 问题:
- 数据修改不方便
- 代码冗余
- 解决方案:
- 使用自定义类实现数据类
- 问题:
class Cat:
name: str
color: str
weight: int
def __init__(self,name,weight,color):
self.name = name
self.weight = weight
self.color = color
def __str__(self):
return f"喵星人姓名:{self.name}, 年龄:{self.weight},颜色:{self.color}"
# repr方法:返回一个对象的 string 格式
def __repr__(self):
return f"===>>>>> 喵星人姓名:{self.name}, 年龄:{self.weight},颜色:{self.color}"
if __name__ == '__main__':
cat = Cat("大橘", 10, "橘色")
print(cat) # 喵星人姓名:大橘, 年龄:10,颜色:橘色
数据类更优雅的实现方案
- 使用dataclass创建数据类
- 实例化的时候自动生成构造函数(实例化的时候只要传入变量就可以)
from dataclasses import dataclass
# 1. 加装饰器@dataclass
# 2. 为变量添加类型提示
@dataclass
class Cat:
name: str
color: str
weight: int
if __name__ == '__main__':
cat = Cat("菠萝", "橘猫", 9)
print(cat) # Cat(name='菠萝', color='橘猫', weight=9)
注意:必须要指定类型,否则不会添加到init方法中
field的使用
作用:指定参数的默认值
如果是不可变类型,那么直接定义和使用filed定义都是一样的
from dataclasses import dataclass, field
# 1. 加装饰器@dataclass
# 2. 为变量添加类型提示
@dataclass
class Cat:
name: str
color: str
# weight: int = 2
weight: int = field(default=2)
# 错误写法,执行报错
@dataclass
class Cat:
name: str
color: str
weight: int
children: list=[1,2,3]
# 正确写法,可变类型必须使用field
from dataclasses import dataclass, field
@dataclass
class Cat:
name: str
color: str
weight: int = 2
# 可变参数 list,dict,需要通过default_factory来指定类型或者默认值
children: list = field(default_factory=list)
if __name__ == '__main__':
cat = Cat("菠萝", "橘猫", 9, [1,2,3])
filed常用参数
参数名 参数功能
default 字段的默认值
default_factory 定义可变量参数的默认值,default 和 default_factory 不能同时存在
init 如果为 true(默认值),该字段作为参数包含在生成的 init() 方法中。
repr 如果为 true(默认值),该字段包含在生成的 repr() 方法返回的字符串中。
- field default 参数
字段的默认值
import dataclasses
@dataclasses.dataclass
class Cat:
name: str
color: str
weight: str = dataclasses.field(default=5)
children: list = dataclasses.field(default_factory=list)
children1: list = dataclasses.field(default_factory=lambda:[1,2,3])
children2: dict = dataclasses.field(default_factory=lambda: {"name":"喵"})
- field init 参数
如果为 True(默认值),该字段作为参数包含在生成的 init() 方法中。
如果为 False,该字段不会包含 init() 方法参数中。但是前提是要有默认值。
import dataclasses
@dataclasses.dataclass
class Cat:
name: str
color: str
weight: str = dataclasses.field(default=5)
children: list = dataclasses.field(default_factory=list,init=False)
- field repr 参数
如果为 True(默认值),该字段包含在生成的 repr() 方法返回的字符串中。
如果为 False,该字段不会包含在生成的 repr() 方法返回的字符串中。
import dataclasses
@dataclasses.dataclass
class Cat:
name: str
color: str
weight: str = dataclasses.field(default=5)
children: list = dataclasses.field(default_factory=list,repr=False)
常用方法
asdict() 转化实例对象为字典格式
from dataclasses import dataclass, field, asdict
@dataclass
class Cat:
name: str
color: str
weight: int = field(default=5)
# 无参的函数
children: list = field(default_factory=lambda:[1,2,3])
children1: dict = field(default_factory=lambda: {"name":"喵"})
cat = Cat("aa","red",10,[1,3],{"name":"喵喵"})
asdict(cat)
Python内置装饰器
内置装饰器
- 不用实例化、直接调用
- 提升代码的可读性
内置装饰器 含义
classmethod 类方法
staticmethod 静态方法
普通方法
- 定义:
第一个参数为self,代表 实例本身 - 调用:
要有实例化的过程,通过 实例对象.方法名 调用
# 1. 定义
class MethodsDemo:
param_a = 0 #类变量
def normal_demo(self): # 定义一个类方法,第一个参数必须为self
"""
普通方法
:return:
"""
print("这是一个普通方法", self.param_a)
# 2. 调用
md = MethodsDemo()
md.normal_demo()
类方法
- 定义:
使用 @classmethod 装饰器,第一个参数为类本身,所以通常使用cls命名做区分(非强制)
在类内可以直接使用类方法或类变量,无法直接使用实例变量或方法 - 调用:
无需实例化,直接通过 类.方法名 调用,也可以通过 实例.方法名 调用
# 1. 类的定义
class MethodsDemo:
param_a = 0
# 定义类方法必须加 classmethod装饰器
@classmethod
def classmethod_demo(cls):
"""
类方法,第一个参数需要改为cls
:return:
"""
print("这是一个类方法", cls.param_a)
# 2. 类的调用
MethodsDemo.classmethod_demo() # 无需实例化,直接调用
# 1. 类的定义
class MethodsDemo:
param_a = 0
def __init__(self):
self.b = "cba"
def demo_method(self):
print("这是一个普通方法")
def demo_method2(self):
self.demo_method()
self.a = "abc"
print("这是一个普通方法")
# 定义类方法必须加 classmethod装饰器
@classmethod
def classmethod_demo(cls):
cls.demo_method() # 类方法内,不可以直接调用实例变量与实例方法
cls.a # type object 'MethodsDemo' has no attribute 'a'
cls.b # type object 'MethodsDemo' has no attribute 'b'
cls.classmethod_demo2() # 类方法内,可以直接调用类变量与类方法
print("这是一个类方法", cls.param_a)
@classmethod
def classmethod_demo2(cls):
print("这是一个类方法", cls.param_a)
# 2. 类的调用
MethodsDemo.classmethod_demo() # 无需实例化,直接调用
实例变量、实例方法与类变量、类方法的辨析
实例方法:不加@classmethod装饰器
实例变量:定义在实例方法里面
类方法:加@classmethod装饰器
类变量:定义在类里,方法外面
静态方法
- 定义:
使用 @staticmethod 装饰器,没有和类本身有关的参数
无法直接使用任何类变量、类方法或者实例方法、实例变量 - 调用:
无需实例化,直接通过 类.方法名 调用,也可以通过 实例.方法名 调用
普通方法、类方法、静态方法总结
名称 | 定义 | 调用 | 关键字 | 使用场景 |
---|---|---|---|---|
普通方法 | 至少需要一个参数self | 实例名.方法名() | 无 | 方法内部涉及到实例对象属性的操作 |
类方法 | 至少需要一个cls参数 | 类名.方法名() 或者实例名.方法名() | @classmethod | 如果需要对类属性,即静态变量进行限制性操作 |
静态方法 | 无默认参数 | 类名.方法名() 或者实例名.方法名() | @staticmethod | 无需类或实例参与 |
实际案例
右边的代码实现的需求是格式化输出时间
如果现在需求变更,输入 年、月、日 没法保证格式统一,可能是json,可能是其他格式的字符串,在不修改构造函数的前提下,如何更改代码
最普通的方法
class DateFormat:
def __init__(self, year=0, month=0, day=0):
self.year = year
self.month = month
self.day = day
def out_date(self):
return f"输入的时间为{self.year}年,{self.month}月,{self.day}日"
def json_format(json_data):
year, month, day = json_data["year"], json_data["month"], json_data["day"]
return year, month, day
year, month, day = json_format({"year":2021, "month":12, "day":24})
demo = DateFormat(year, month, day)
print(demo.out_date())
但是这样的改法,使用这个类的人还要去找到这个转换函数,使用起来不够方便
—>可以使用类方法,代码的可读性更强,可维护性也更高
class DateFormat:
def __init__(self, year=0, month=0, day=0):
self.year = year
self.month = month
self.day = day
def out_date(self):
return f"输入的时间为{self.year}年,{self.month}月,{self.day}日"
@classmethod
def json_format(cls, json_data):
"""
输入一个字典格式的数据信息,返回(2021, 1, 2)
:return:
"""
year, month, day = json_data["year"], json_data["month"], json_data["day"]
# return cls(year, month, day)
return DateFormat(year, month, day)
demo = DateFormat.json_format({"year":2021, "month":12, "day":24})
print(demo.out_date())
# demo = DateFormat(year, month, day)
# print(demo.out_date())
静态方法实际案例
常用在:
- 此方法没有任何和实例、类相关的部分,可以作为一个独立函数使用
- 某些场景下,从业务逻辑来说又属于类的一部分
例子:简单工厂方法(可以当学到设计模式那章节再看)
class HeroFactory:
# staticmethod 使用场景,
# 方法所有涉及到的逻辑都没有使用实例方法或者实例变量的时候
# 伪代码
@staticmethod
def create_hero(hero):
if hero == "ez":
return EZ()
elif hero == "jinx":
return Jinx()
elif hero == "timo":
return Timo()
else:
raise Exception("此英雄不在英雄工厂当中")
另一个案例:
# static 使用场景
"""
多轮比赛,每轮由两个不同的英雄对打
"""
class Game:
def __init__(self, first_hero, second_hero):
self.first_hero = first_hero
self.second_hero = second_hero
# fight方法有和实例变量交互的部分,所以需要定义为一个普通方法
def fight(self):
print(f"本轮比赛开始,由{self.first_hero}VS{self.second_hero}")
# start方法没有用到任何和实例、类相关的部分,那么就可以当作静态方法使用
@staticmethod
def start():
print("游戏开始")
Game.start()
game1 = Game("Bob", "Merry")
game2 = Game("Mike", "Henry")
闭包函数
闭包函数是Python的一个高级特性
装饰器的很多思想都是来源于闭包函数
闭包函数的定义:有一个外函数和内函数
函数引用
- 函数可以被引用
- 函数可以被赋值给一个变量
def hogwarts():
print("hogwarts")
harry = hogwarts # 函数加()是函数调用,不加()是函数对象
harry()
闭包函数
- 闭包的内部函数中,对外部作用域的变量进行引用
- 闭包无法修改外部函数的局部变量
- 闭包可以保存当前的运行环境
# 闭包的内部函数中,对外部作用域的变量进行引用
def output_student(grade):
def inner(name, gender):
print(f"霍格沃兹测试学社开学啦!\
学生的名称是{name},性别是{gender},
\年级是{grade}") # grade就是对外部作用域的变量进行引用
return inner
student = output_student(1)
student("罗恩", "男")
student("哈利", "男")
student("赫敏", "女")
比如下面的代码,三个人都是一年级,那么可以进行封装
"""
闭包函数:霍格沃兹开学啦,要求打印每个学生的姓名、性别、年纪
"""
def output_students(name, gender, grade):
print(f"霍格沃兹开学啦, 学生名称是{name}, 性别是{gender}, 年纪是{grade}")
output_students("哈利", "男生", 1)
output_students("罗恩", "男生", 1)
output_students("赫敏", "女生", 1)
代码编写的原则是写好后最好不要去动它的逻辑
"""
闭包函数:霍格沃兹开学啦,要求打印每个学生的姓名、性别、年纪
"""
# def output_students(name, gender, grade=1):
# print(f"霍格沃兹开学啦, 学生名称是{name}, 性别是{gender}, 年纪是{grade}")
#
# output_students("哈利", "男生", 2)
# output_students("罗恩", "男生", 2)
# output_students("赫敏", "女生", 2)
def students_grade(grade):
def output_students(name, gender):
print(f"霍格沃兹开学啦, 学生名称是{name}, 性别是{gender}, 年纪是{grade}")
# 需要把函数对象return出来
return output_students
student_info = students_grade(2) # 此时已经指向output_students对象
student_info("哈利", "男生")
student_info("罗恩", "男生")
student_info("赫敏", "女生")
闭包函数使得我们可以在函数外部使用函数内的局部变量,使得变量的状态得以持久化,并且在不同的上下文中使用这些变量,这是闭包函数的一个重要作用。
# 闭包无法修改外部函数的局部变量
def students_grade(grade):
grade = "2"
print("外函数的年级为", grade)
def output_students(name, gender):
grade = "1"
print("内函数的年级为", grade)
# print(f"霍格沃兹开学啦, 学生名称是{name}, 性别是{gender}, 年纪是{grade}")
# 需要把函数对象return出来
return output_students
student_info = students_grade(2) # 此时已经指向output_students对象
student_info("哈利", "男生")
student_info("罗恩", "男生")
student_info("赫敏", "女生")
Python装饰器
- 为什么要学习装饰器
- 行业需求:涉及Python技术栈,面试常见题
- 使用需求:优化代码的可读性,可维护性
装饰器示例
- 函数体开始执行与结束执行的时候分别添加打印信息
首先第一部优化,封装函数
第二部优化,把中间的执行函数,使用参数代替
def hogwarts():
print("霍格沃兹测试学社")
def hogwarts2():
print("霍格沃兹测试学社2部")
def timer(func):
print("函数开始执行")
# 不再是写死的任何一个函数,而是任意外部传入的函数对象
func()
print("函数结束执行")
timer(hogwarts())
# 不使用装饰器的代码
def timer(func):
print("计时开始")
func()
print("计时结束")
def hogwarts():
print("霍格沃兹测试学院")
timer(hogwarts)
# 使用装饰器的代码
# 如何去定义一个装饰器?装饰器的本质就是一个闭包函数,闭包函数的定义就是有一个内函数和一个外函数
# 第一步,定义两个函数,一个内函数,一个外函数
# 第五步,在装饰器执行过程中,会自动传入一个参数,参数就是被装饰函数的函数对象(注意形参是定义在外函数中)
def timer(func):
def inner():
# 第二步,在内函数添加装饰器
print("计时开始")
func() # 第六步,添加被装饰函数的执行步骤
print("计时结束")
# 第三步,把内函数的函数对象return出去,如果不把内函数对象return出去它是永远无法被调用到的
return inner
# 在被监测的函数上面加
# 第四步,装饰器的使用
@timer
def hogwarts():
print("霍格沃兹测试学院")
@timer
def hogwarts2():
print("霍格沃兹测试学院")
hogwarts()
hogwarts2()
执行步骤:hogwarts()->执行timer(),将hogwarts()-作为参数传入func中->执行return inner->执行inner()
装饰器练习
- 实现一个计时器的装饰器,计算函数执行时间
最简单的装饰器定义三步走:
第一步,定义一个外函数,外函数有一个形参,接受被装饰的函数对象
第二部,定义一个内函数,内函数内调用传入函数
第三步,定义外函数的返回值,外函数返回值归档格式为内函数对象
在不改变函数原有逻辑的基础上改变函数
import datetime
def timer(func):
def inner():
# 获取当前时间
time1 = datetime.datetime.now()
func()
# 计算时间
time2 = datetime.datetime.now()
time_ac = time2 - time1
print(time_ac)
return inner
@timer
def hogwarts():
print("霍格沃兹")
hogwarts()
装饰器带参数
import datetime
def timer(func):
# 如果被装饰函数有参数,那么需要在内函数加形参以及,在函数参数调用的时候添加参数信息
# 如果写死一个参数,但是无法确定被装饰函数的参数数量,这种写法就不行,会报错
# 解决方案:把两个地方的参数全部换成不定长参数
def inner(*args, **kwargs):
# 获取当前时间
time1 = datetime.datetime.now()
func(*args, **kwargs)
# 计算时间
time2 = datetime.datetime.now()
time_ac = time2 - time1
print(time_ac)
return inner
@timer
def hogwarts(name, age, gender):
print("霍格沃兹", name, age, gender)
hogwarts("harry", 11, '男')
关于可变参数的解释