前言
我的Python都是散学,没有系统性地学习过,感觉需要写代码的时候越来越多了,正好趁着最近比较有空闲,好好的学习一番。
规划一下路径,基础语法,函数与模块,面对对象的编程,异常处理,标准库和第三方库的使用,实际运用
学习资源推荐:
菜鸟编程(Pyhton)
编程笔记:
https://tutorials.wcode.net/python
b站上的黑马程序员的Pyhton篇
VScode的官方文档:https://code.visualstudio.com/docs/languages/Python
Pycharm的官方文档:https://www.jetbrains.com/zh-cn/help/pycharm/quick-start-guide.html
也不知道大家是如何提高自己写代码的能力的,请大家不吝赐教!!!
基础
其实大家都说,写代码最重要的就是调试,我此次最需要学习的就是这一点了。真正的大佬都是返璞归真的,有些人使用的是Pyhton的原始编辑器,大家可以试试!!!
Pyhton3
给大家讲这个是因为之前我打比赛的时候有一个密码学的题目(题目忘记了)大概就是要使用Python2进行部署然后解决问题,其实Python2和Python3还是有一点点差别的。
相比于Python2,Python3默认使用Unicode字符串,修复了一些语法的问题,性能优化了,更新了很多第三方的库,提供了更好的异常机制,使得代码的健壮性提高了。
**Python2和Python3最明显的差别就是打印函数和整数除法的语法。**在Python中,print语句来输出内容,整数除法操作符(/)执行的是向下取整法(floor division)而在Python3中,我们使用print()函数,(/)表示的是真正的除法。
如何安装这个Python3呢?
一、使用包管理安装器(pip,apt,yum或者homebrew,根据不同的操作系统来选哦!)
二、使用pyenv安装 。pyenv是一种流行的Python版本管理工具,允许我们在同一台机器上安装和使用不同版本的Pyhton,保证我们一直使用的是最新版本的Pyhton3,并不会影响系统的其他功能
如何安装和Hello,World!就不写了。
注释
1.单行注释:以#开头的文本,直到行尾结束。
2.多行注释:使用三个连续的单引号或双引号包围的文本,通常放在函数、类或块的定义之后,作为对它们的描述
3.函数注解:提供关于函数参数和返回值的元数据
函数注解是Python3.5引入的一种新的特性,它允许我们作为函数参数和返回值添加元数据。
def greet(name:str) -> str:
return f"Hello,{name}!"
-> str 表示函数的返回类型是一个字符串,而name:str表示参数name应该是一个字符串。然而,Python解释器并不会强制执行注解中的类型信息
这个有点像Rust的写法!
变量
Python中无需声明变量即直接开始使用。只需要为变量赋予一个值,Python解释器就会根据该值的类型自动创建并分配内存,当变量不再被引用时,Python会自动将其从内存中删除
在变量名的规则之中,变量是区分大小写,不能以数字开头,只能包含字母,数字和下划线。
x=y=x="Python"
数据类型:
整数(Integer): 用于存储整数值,例如 x = 10。
浮点数(Float): 用于存储带小数部分的数字,例如 y = 20.5。
字符串(String): 用于存储文本数据,例如 name = "John Doe"。
列表(List): 用于存储有序、可变的元素集合,例如 my_list = [1, 2, 3]。
元组(Tuple): 用于存储有序、不可变的元素集合,例如 my_tuple = (1, 2, 3)。
字典(Dictionary): 用于存储键值对,例如 my_dict = {"name": "John", "age": 30}。
变量的作用域分为"全局变量"和"局部变量",只能在该函数内使用,如果您在函数中需要修改全局变量,则必须global关键字
数字类型
Number
整数(integer)、浮点数(float)
x=10
print(type(x))
y=3.13
print(type(y))
序列(Swquence)
字符串(string)
我们可以使用单引号,双引号或三引号来定义Python中的字符串
s="Hello,world!"
print(s[0]) #输出:'H'
print(s[7:12]) #输出:'world'
str.upper()和str.lower():将所有字母转换为大写或小写
str.split(sep):根据指定的分隔符分隔字符串,返回了一个包含子字符串的列表
str.replace(old,new):用新字符串替换字符串中所有出现的旧字符串
str.strip():删除开头和结尾处的空白字符
str.startswith(prefix)和str.endwith(suffix):检查字符串是否以指定前缀或后缀开头/结尾。
复数由实部和虚部组成
x=3+4j
y=complex(1,2) #也可以使用complex()函数创建复数
列表(List),这一个可变的序列,用于储存不同类型的项目、元组(Tuple)
s="Hello,World!"
print(type(s)) #<class 'str'>
l=[1,"hello",3.4]
ptint(type(l)) #<class 'list'>
t=(1,"hello",3.4)
print(type(t)) #<class 'tuple'>
我们要进行修改列表元素,删除,增加
#修改列表元素
fruits=['apple','banana','cherry']
fruits='mama'
print(fruits)
#在末尾添加了新元素
fruits = ['apple', 'banana', 'cherry']
fruits.append('ora')
print(fruits)
#insert()方法在列表的特定位置插入新的元素
fruits=['apple','banana','cherry']
fruits.insert(1,'mango')
print(fruits)
#删除remove()
fruits = ['apple', 'banana', 'cherry']
fruits.remove('banana')
print(fruits)
del关键字根据索引来删除列表中的元素
fruits = ['apple', 'banana', 'cherry']
del fruits[1]
print(fruits
映射(Mapping)
这是一种无序的数据集合,其中每个元素都存储为键值对。
字典(Dictionary)
d ={"name":"John","age":30}
print(type(d))
我们也可以使用dict()构造函数来创建字典
my_dict=dict(name='John',age='30',city='New York')
print(my_dict)
我们使用方括号[]来提供对应的键作为索引
如果我们访问不存在的键时,Python将引发一个KeyError异常,为了避免使用这个方法get()方法来获取值,如果指定的键不存在在于字典中,get()方法将返回none或提供的默认值。
#修改字典元素
my_dict['age'] =31
print(my_dict)
要向字典中添加新的键值对,只需为不存在的键分配一个值。
#添加字典元素
my_dict['country'] ='USA'
print(my_dict)
删除字典元素
del my_dict['city']
print(my_dict)
my_dict.pop('age')
print(my_dict)
迭代字典
for key in my_dict:
print(key)
使用for循环来迭代字典的键、值或键值对
集合(Set)
这是提个可变的集合,用于存储不同类型的项目。集合是用花括号{}或set()函数创建的元素序列
s={1,2,3}
print(type(s))
冻和集(Frozenset):这是一个不可变的结合,用于存储不同类型的项目。
fs=frozenset({1,2,3})
print(type(fs)) #<class 'frozenset'>
交:使用&或者intersection()方法可以计算两个集合的交集
set1={1,2,3}
set2={2.3,4}
print(set1 & set2) #输出:{2,3}
并:使用|或者union()方法可以计算两个集合的并集
print(set1 | set2) #输出:{1,2,3,4}
差:使用-或者difference()方法可以计算两个集合的差集
print(set1 -set2)
布尔(Boolean)
Python3中的布尔值只有两种可能:True和False
b=True
print(type(b))
我们的布尔值可以进行运算:
and :如果操作数都为True,则返回True;返回False
not:如果任何一个操作数True,则返回True;只有两个操作数都为False,则返回False
or:用于反转布尔值的真假,如果操作数是True,则返回False,操作数是False,则返回True
None
n=None
print(type(n))
运算符
+: 加法,例如 3 + 5 返回 8
-: 减法,例如 10 - 7 返回 3
*: 乘法,例如 4 * 6 返回 24
/: 除法,例如 8 / 2 返回 4.0 (注意,Python 3中的除法总是返回浮点数)
%: 取模(返回余数),例如 15 % 4 返回 3
**: 幂运算,例如 2 ** 3 返回 8 (2的三次方)
//: 整数除法(返回商的整数部分),例如 10 // 3 返回 3
=: 赋值,例如 x = 10
+=: 加法赋值,等同于 x = x + ...,例如 x += 5
-=: 减法赋值,等同于 x = x - ...,例如 x -= 3
*=: 乘法赋值,等同于 x = x * ...,例如 x *= 2
/=: 除法赋值,等同于 x = x / ...,例如 x /= 4
%=: 取模赋值,等同于 x = x % ...,例如 x %= 5
**=: 幂运算赋值,等同于 x = x ** ...,例如 x **= 3
//=: 整数除法赋值,等同于 x = x // ...,例如 x //= 2
==: 等于,例如 5 == 3 返回 False
!=: 不等于,例如 10 != 10 返回 False
<: 小于,例如 4 < 9 返回 True
>: 大于,例如 7 > 2 返回 True
<=: 小于或等于,例如 3 <= 3 返回 True
>=: 大于或等于,例如 5 >= 4 返回 True
if...else语句、while语句、for循环这些就不讲了,来讲讲一些特殊的东西
match...case
这是Python3.10版本引入的新语法,match 后的对象会依次与 case 后的内容进行匹配,如果匹配成功,则执行匹配到的表达式,否则直接跳过,_ 可以匹配一切
match expression:
case pattern1:
#处理pattern1的逻辑
case pattern2 if condition:
#处理pattern2并满足condition的逻辑
case _:
#处理其他情况的逻辑
例子:
def match_example(item):
match item:
case (x,y) if x==y:
print(f"匹配到相等的元组:{items}")
case (x,y):
print(f"匹配到元组:{items}")
case_:
print("匹配到其他情况")
match_exemple((1,1))
match_exemple((2,1))
match_exemple("other")
数组
Python中的数组是通过array模块实现
import array as arr
使用array()函数来创建数组,该函数接受两个参数:元素类型码和初始值列表
my_array =arr.array('i',[1,2,3,4,5])
'i'是元素类型码,表示整数,'f'(浮点数),'u'(Unicode字符)等
print(my_array[0]) #输出:1
my_array[0] =10
print(my_array) #输出:array('i',[10,2,3,4,5])
#我们向数组中添加元素,使用append()方法
my_array.append(6)
print(my_array) #输出:array('i',[10,2,3,4,5,6]
#删除某个元素,使用pop()方法
my_array.pop(0)
print(my_array) #输出:array('i',[2,3,4,5,6])
insert(i,x):在指定位置i插入元素x
extend(iterable):将可迭代对象中的所有元素添加到数组末尾
remove(x):删除第一个出现的元素x
index(x):返回第一个出现的元素x的索引
count(x):返回元素x在数组中出现的次数
Python函数
定义
我们使用def来定义一个函数,def后接函数标识符名称和圆括号
def greet(name):
print("Hello,"+ name)
调用
定义一个函数时只给了函数一个名称,指定了函数里包含的参数和代码块结构,这个结果是固定的,所以我们调用时可以快速使用
greet("WOrld")
我们调用greet函数并传递了"WOrld"作为参数
传递
在Python中,类型属于对象,变量是没有类型的
我们在Python中,可更改对象(list,dict)和不可更改对(strings,tuples,numbers)
这是这么理解的不可改变对象,a=5后再赋值a=10,由于我们的代码是串行的,两条语句都能执行,新生成一个int值对象10,再让a指向他,a=5后面是被丢弃了,不是改变a的值,
Python中一切都是对象,严格上来说我们不能说值传递还是引用传递,应该说传不可变对象和传可变对象
a=[1,2,3]
a="Runoob"
【1,2,3】是list类型,"Runoob"是String类型,a没有类型,它仅仅是一个对象的引用
我们在调用函数时,一定要传入参数,不然会出现语法错误,调用时使用关键字参数来确定传入的参数值,Python解释器能用参数名匹配参数值
#传不可变对象
def ChangeInt(a):
a=10
b=2
ChangeInt(b)
print b #结构是b
'''int对象2,指向它的变量是b,在传递给ChangeInt函数时,按传值的方式复制给了b,a和b都指向了同一个Int对象,在a=10,则新生成一个Int值对象10,并让a指向它。'''
#传可变对象
def changeme(mylist):
"修改列表"
mylist.append([1,2,3,4])
print("函数内取值:",mylist)
return
mylist=[10,20,30] #调用changme函数
changeme(mylist)
print ("函数外取值:",mylist)
结果:
函数内取值:[10,20,30,[1,2,3,4]]
函数外取值:[10,20,30,[1,2,3,4]]
返回
函数可以通过使用return语句来返回一个值,不带表达式的return相当于返回None
def sum(ag1,ag2):
total=ag1+ag2
print("函数内:",total)
return total
#调用
total=sum(10,20)
print("函数外:",total)
>>>
函数内:30
函数外:30
x=True
def printLine(text):
print(text,'Runoob')
printLine('Python')
>>>
Python Runoob
Python3.8新增了一个函数形参语法/用了指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。
def f(a,b,/,c,d,*,e,f):
print(a,b,c,d,e,f)
形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 和 f 要求为关键字形参
f(10, 20, 30, d=40, e=50, f=60)
f(10, b=20, c=30, d=40, e=50, f=60) # b 不能使用关键字参数的形式
f(10, 20, 30, 40, 50, f=60) # e 必须使用关键字参数的形式
作用域
Python遵循LEGB规则来决定变量名称引用那个值,
Lambda函数
特性:
一般用来创建匿名函数,lamdba函数只包含一个语句,并且该语句的执行结果将被作为这个小型函数的返回值
用法:
lamdba arguments:expression
例子:
1.简单的加法
add =lambda x,y:x+y
print(add(3,5)) #8
2.使用Lambda函数作为另一个函数的参数
numbers=[1,2,3,4,5]
squared_numbers=list(map(lambda x:x**2,numbers))
print(squared_numbers) #[1,4,9,16,25]
3.在Lambda函数中使用if-else语句
is_even =lambda x:"Yes" if x % 2 ==0 else "No"
print(is_even(4)) #Yes
print(is_even(5)) #No
#我们定义了一个Lambda函数来检查一个数字是否为偶数,如果是,则返回"Yes";否则,返回"No"
编写复杂的函数或者需要多行代码的时候就可以使用普通函数来定义。
我们还有可以把匿名函数封装在一个函数中,这样代码就可以使用同样的代码来创建多个匿名函数了。
def myfunc(n):
return lambda a: a*n
my2=myfunc(2)
my3=myfunc(3)
print(my2(11))
print(my3(11))
>>>
22
33
装饰器
本质上是一个高阶函数,接受一个函数作为参数并返回一个新的修改过的函数。装饰器允许我们在不直接修改被装饰函数代码的情况下添加额外功能,例如日志、权限验证等。
使用**@decorator_name来应用在函数或方法上,PYthon还提供了一些内置的装饰器,比如:@staticmethod和@calssmethod,用于定义静态方法和类方法**
应用的场景:
日志记录:记录函数的调用信息、参数和返回值
性能分析:测量函数的执行时间
权限控制:限制某些函数的访问权限
缓存:实现函数结果的缓存
def decorator_func(original_func):
def wrapper(*args,**kwargs):
#调用前执行的代码
before_call_code()
#调用原始函数
result = original_func(*args,**kwargs)
#调用执行后的代码
after_call_code()
return result
return wrapper #返回了wrapper函数本身
---------------------------------------------------------
#使用装饰器
@decorator_func
def target_func(arg1,arg2):
pass #原始函数的实现
-----------------------------------------------------------
中间这个相当于:
target_func =decorator_func(target_func)
decorator_func:装饰函数
wrapper:内部包装函数,添加新功能的地方
original_func:被装饰的原始函数
我们练习了一下:

这个是带参数的

进行对比

这个是利用Python自带的装饰器

练习
从某leetcode上找了一个题来练手,155最小栈,设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

原本的解是这样的:
class MinStack:
def __init__(self):
self.stack=[]
self.Min_Stack = []
def push(self, val: int) -> None:
self.stack.append(val)
#检查MIn_Stack是否为空,或者val是否小于当前最小值
if not self.Min_Stack or val <= self.Min_Stack[-1]:
self.Min_Stack.append(val)
def pop(self) -> None:
if self.stack:
#先弹出主栈元素,然后检查是否应该弹出最小栈
poppped_val = self.stack.pop()
if self.Min_Stack and poppped_val == self.Min_Stack[-1]:
self.Min_Stack.pop()
def top(self) -> int:
return self.stack[-1] if self.stack else None
def getMin(self) -> int:
return self.Min_Stack[-1] if self.Min_Stack else None

这是大神的写法(哨兵+元组),附上,一同钻研一下
大佬的栈使用了一个栈,不过栈存储的是元组(value,current------min),每次push时,同时记录当前值以及当前的最小值,我们栈顶的第二个值就是当前栈的最小值。
初始化的是否,先放一个哨兵,值(0,inf),这样,我们的栈永不为空,同时inf(无穷大)保证任何值都比它下。
push时,将val和当前的最小值进行比较并取较小者一起入栈,每个元素都记住了从栈底到该元素的最小值
pop时,直接弹出栈顶即可,因为每个元素都记录了它入栈时的最小值
top时,返回栈顶元素的value部分
getMin时,返回栈顶元素的current_min部分
这个还有一个优点,这个的时间复杂度都是O(1),利用了空间来换代码的简洁度
我们直接用上自带的装饰器后,有问题,我们的top,getMin变成了属性而不是方法,无法直接修改,简单的方式是对我们的测试代码进行修改,但是没办法,所以这个练习只能练练出栈和入栈了

class MinStack:
def __init__(self):
self.stack = []
self.Min_Stack = []
def push(self, val: int) -> None:
#入栈
self.stack.append(val)
if not self.Min_Stack or val <= self.Min_Stack[-1]:
self.Min_Stack.append(val)
def pop(self) -> None:
#出栈
if not self.stack:
return # 空栈直接返回
popped_val = self.stack.pop()
if self.Min_Stack and popped_val == self.Min_Stack[-1]:
self.Min_Stack.pop()
@property
def top(self) -> int:
"""获取栈顶元素(作为属性访问)"""
return self.stack[-1] if self.stack else None
@property
def min(self) -> int:
"""获取最小值(作为属性访问)"""
return self.Min_Stack[-1] if self.Min_Stack else None
@property
def is_empty(self) -> bool:
"""检查栈是否为空(作为属性访问)"""
return len(self.stack) == 0
@property
def size(self) -> int:
"""获取栈的大小(作为属性访问)"""
return len(self.stack)
迭代器和生成器
Python中的迭代器是一个可以被遍历的对象,它能返回序列中的元素一个接一个地进行迭代。迭代对象从集合的第一个元素开始访问,直到所有的元素都被访问结束。迭代器只能向前不能后退。
创建一个迭代器很简单,只需要实现两个方法:
iter()方法返回一个特殊的迭代器,这个迭代器对象实现了__next__()方法并通过Stoplteration异常标识迭代的完成;
next()会返回下一个迭代对象
class MyIterator:
def __init__(self,data):
self.data =data
self.index =0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
result = self.data[self.index]
self.index +=1
return result
__next__方法返回序列中的下一个元素,如果没有更多的元素,则应引发StopIteration异常
生成器:
我们来学一下这个if name = "main"
直接学习这个脚本:

如果我们引入了

在Python中,使用了yield的函数被称为生成器
yield是一个关键字,用于定义生成器函数(一种返回迭代器值的函数),可以在迭代的过程中逐步产生值,而不是一次性返回所有结果的值,当在生成器函数中使用 yield 语句时,函数的执行将会暂停,并将 yield 后面的表达式作为当前迭代的值返回 。每次调用生成器的 next() 方法或使用 for 循环进行迭代时,函数会从上次暂停的地方继续执行,直到再次遇到 yield 语句。这样,生成器函数可以逐步产生值,而不需要一次性计算并返回所有结果
def countdown(n):
while n > 0:
yield n
n -=1
generator =countdown(5)
#通过迭代器生成器获取值
print(next(generator)) #输出:5
print(next(generator)) #输出:4
print(next(generator)) #输出:3
for value in generator:
print(value)
#输出:2,1
如果利用yield实现斐波那契数列:

import sys
def fibncode(n):
a,b,counter =0,1,0
while True:
if (counter >n ):
return
yield a
a,b =b,a+b
counter +=1
f=fibncode(10) #f是一个迭代器,由生成器返回生成
while True:
try:
print(next(f),end=" ")
except StopIteration:
sys.exit()