天天在写python,但是感觉自己对python的高级用法不是特别了解,此处浅析一下。
1 python类与对象
1.1 专业的类书写规范
类中比较专业的规范为:
- 1 类属性
- 2 __init__初始化方法
- 3 实例属性
- 4 classmethod方法
- 5 staticmethod方法
- 6 私有方法
- 7 实例方法
classmethod方法与staticmethod方法:
- 1 classmethod方法涉及使用类中的资源
- 2 staticmethod方法一般用来实现固定的操作,一般不涉及类中的资源的操作。
举例,我们写一个学生信息类,将以上的方法都实现一遍。
class Student(object):
# 1 类属性
_ID_TYPE: str = "Student Person"
# 2 __init__初始化方法
def __init__(self, name, score, age):
# 3 实例属性
self._name: str = name
self._score: int = score
self._age: int = age
# 4 classmethod方法: 能操作类的全部资源
@classmethod
def get_student(cls):
return cls("tom", "100", 18)
# 5 staticmethod方法: 只能操作类的实例
@staticmethod
def print_student_type():
print(f"name is {Student._ID_TYPE}")
# 6 私有方法: 对外层用户透明
def _set_age(self, age):
if age > 100:
self._age = 100
elif age < 0:
self._age = 0
else:
self._age = age
# 7 实例方法
@property
def score(self):
return self._score
@score.setter
def score(self, score):
self._score = score
def set_age(self, age):
self._set_age(age)
1.2 类的私有成员保护
python实际上没有私有成员,大家公认的是实例或者类的私有成员(变量和方法)在前面加上下划线(IDE将不会帮助提示)。
如果类或者实例中的属性不设置私有,则其他程序员可以通过赋值的方式修改属性。
解决方法:将属性设置为私有属性,同时提供@proptity提供访问接口。
实现对私有成员的访问,同时不提供修改的策略。
1.3 抽象类
抽象类继承abc.ABC,同时设置了@abstractmethod的方法子类必须重写。
1.4 魔法方法
类的函数中左右两边都存在两个下划线的即为魔法方法。
1.5 __str__和__repr__
student = Student.get_student() print(student)
此时对象的print的结果就会去__str__函数进行处理。
student1 = Student.get_student() student2 = Student.get_student() print([student1, student2])
在列表、元组、集合中打印的对象会去__repr__函数进行处理。
2 装饰器
装饰器(Decorator)是Python中一种用于修改函数或方法行为的强大工具。装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。装饰器通常用于在不修改原函数代码的情况下,增加函数的功能,比如日志记录、性能监控、权限检查等。
装饰器的工作原理基于Python的函数是第一类对象(First-Class Object)这一特性,即函数可以作为参数传递给其他函数,也可以作为返回值从函数中返回。
装饰器分为两部分:
- 1 装饰器的输入和输出
- 输入:oldFunction
- 输出:newFunction
- 2 装饰器的加工过程
- newFunction对oldFunction进行加工
2.1 不带参数的装饰器
- 1 装饰器输入一个旧函数,抛出一个新函数
- 2 装饰器在新函数中对旧函数进行加工
def robot(oldFunc):
def newFunc():
print("start")
oldFunc()
print("end")
return newFunc
@robot
def hello():
print("Hello World")
hello()
# start
# Hello World
# end
实际上在@的语法糖下,hello等价为robot(hello),故hello() -> robot(hello)()。
2.2 带参数的装饰器
- 1 装饰器输入一个旧函数,抛出一个新函数
- 2 装饰器在新函数中对旧函数进行加工
- 3 装饰器的新函数入口的参数设置与旧函数保持一致才能进行加工。
def robot(oldFunc):
def newFunc(name):
oldFunc(name[::-1])
return newFunc
@robot
def hello(name):
print(f"Hello {name}")
hello("alice")
# Hello ecila
实际上在@的语法糖下,hello等价为robot(hello),故hello() -> robot(hello)(“alice”)。
2.3 多装饰器
多装饰器的执行顺序为离近原则,从离函数近到远的顺序执行装饰器。
例如,我们给出@runDouble, @logger装饰器.
2.3.1 @logger @runDouble
def runDouble(oldFunc):
def newFunc(*args, **kwargs):
oldFunc(*args, **kwargs)
oldFunc(*args, **kwargs)
return newFunc
def logger(oldFunc):
def newFunc(*args, **kwargs):
print(f"===============")
oldFunc(*args, **kwargs)
print(f"===============")
return newFunc
@runDouble
@logger
def hello(name):
print(f"Hello {name}")
hello("alice")
# ===============
# Hello alice
# ===============
# ===============
# Hello alice
# ===============
2.3.2 @runDouble @logger
def runDouble(oldFunc):
def newFunc(*args, **kwargs):
oldFunc(*args, **kwargs)
oldFunc(*args, **kwargs)
return newFunc
def logger(oldFunc):
def newFunc(*args, **kwargs):
print(f"===============")
oldFunc(*args, **kwargs)
print(f"===============")
return newFunc
@logger
@runDouble
def hello(name):
print(f"Hello {name}")
hello("alice")
#===============
# Hello alice
# Hello alice
# ===============
2.4 双重装饰器(带括号和参数)
双重装饰器(带括号)实际上就是可以等同于使用两次@装饰器修饰.
双重装饰器的实现需要使用2次函数嵌套,具体如下:
def fun(type):
if type == "double":
def runDouble(oldFunc):
def newFunc(*args, **kwargs):
oldFunc(*args, **kwargs)
oldFunc(*args, **kwargs)
return newFunc
return runDouble
elif type == "logger":
def logger(oldFunc):
def newFunc(*args, **kwargs):
print(f"===============")
oldFunc(*args, **kwargs)
print(f"===============")
return newFunc
return logger
@fun("double")
def hello(name):
print(f"Hello {name}")
hello("alice")
# Hello alice
# Hello alice
实际上在@的语法糖下,hello等价为func(“double”)(hello),故hello() -> func(“double”)(hello)(“alice”)。
3 浅拷贝与深拷贝
3.1 普通赋值
a = [1, 2, 3]
b = a
修改a,则b也会跟着修改。
因为a,b都指向同一个对象,即指向同一块内存地址。
a = [1, 2, 3]
b = a
a[0] = 4
print(a, b)
# [4, 2, 3] [4, 2, 3]
3.2 浅拷贝
3.2.1 不带子对象的浅拷贝
a = [1, 2, 3]
b = a.copy()
修改a,b不会跟着修改。
b为一个新的对象,拷贝了a的父对象。
a = [1, 2, 3]
b = a.copy()
a[0] = 4
print(f"a is {a} b is {b}")
# a is [4, 2, 3] b is [1, 2, 3]
3.2.2 带子对象的浅拷贝
a = [1, 2, [3, 4]]
b = a.copy()
修改a[2]中的列表值,b跟着修改。
b虽然为一个新的对象,拷贝了a的父对象,但是不会拷贝子对象[3, 4].
a = [[1, 4, 5], 2, 3]
b = a.copy()
a[0][0] = 9
print(f"a is {a} b is {b}")
# a is [[9, 4, 5], 2, 3] b is [[9, 4, 5], 2, 3]
3.3 深拷贝
from copy import deepcopy
a = [1, 2, [3, 4]]
b = deepcopy(a)
修改a[2]的列表,b不发生改变。
b拷贝了a的父对象和子对象。
import copy
a = [[1, 4, 5], 2, 3]
b = copy.deepcopy(a)
a[0][0] = 9
print(f"a is {a} b is {b}")
# a is [[9, 4, 5], 2, 3] b is [[1, 4, 5], 2, 3]
4 如何多层跳出循环
众所周知,break只能跳出一层循环,如果遇到多层循环该如何直接退出到最外层循环呢。
4.1 try except
最推荐使用的一种方法,无论在多少层for之中,最内层抛出一个raise给except接受即可。
try:
for i in range(10):
for j in range(10):
print(f"i is {i} j is {j}")
if (i == j == 2):
raise "i,j已经满足要求了"
except Exception as e:
print(e)
4.2 for else continue
不太推荐使用,遇到多个for则需要写多个else: continue break
else在这里处于一种语法糖一样的东西
- 1 当for循环执行成功,则执行else中的语句
- 2 当for循环执行不成功,则跳过else中的语句。
- 3 此时,当时for循环中遇到break,则跳过else中的continue,继续执行break。
for i in range(10):
for j in range(10):
print(f"i is {i} j is {j}")
if (i == j == 2):
break
else:
continue
break
5 any和all
5.1 简易使用
any:列表中有一个为True,则返回True
all:列表中有一个为False,则返回False
print(any([True, False, True, False]))
print(all([True, False, True, False]))
# True
# False
5.2 搭配列表使用
判断一个列表中是否存在能整除2的数。
x = [1, 2, 3, 4, 5, 6]
print(any([i%2==0 for i in x]))