python基础语法
很早之前,曾学过一段时间Python;恰好最近工作需要,重新拾起并整理Python相关知识。
参考
Python2与Python3的抉择
参考
JavaScript社区热衷于接收新事物,当出现新的语法规范甚至仅仅只是提案时,尽管是浏览器还未支持的语法,就已经可以使用Babel等工具提前体验了。
但Python3已经出现了很多年,整个Python社区仍有大部分项目使用Python2,但Python2
和Python3
是不兼容的,因此在学习Python时,需要面对的第一个问题就是应该选择哪个Python版本进行学习?
官方宣布2020年不再支持Python2.7
,因此现在学习Python的话直接从Python3开始吧。
基础语法
数据类型
字符串
可以使用三引号跨行输出,常用于做注释
'''comment
hello comment
'''
布尔值
使用True
和False
表示布尔值
a = True
b = False
函数
使用def
自定义函数,函数体的第一行语句可以是可选的字符串文本(使用三个引号可以不需要转义符),这个字符串是函数的文档字符串,或者称为 docstring,经常写注释是一个很好的习惯.
# 支持默认参数
def test(a, b = 10):
"""just for test
"""
print(a, b)
test(1)
# 支持非定长参数,用于获取所有传入的参数
def test3(*numbers):
sum = 0
for n in numbers:
sum = sum + n
return sum
print(test3(1, 2))
print(test3(1, 2, 3))
# 支持rest参数
def test4(sum = 0, *numbers):
# *numbers 获取剩余参数
for n in numbers:
sum = sum + n
return sum
print(test4(1, 2))
print(test4(1, 2, 3))
关于函数参数默认值需要注意的一点是:默认参数只能指向字面值,而不能指向引用对象
# 在函数定义的时候,L的默认值就被计算出来了,每次使用默认参数调用时都会指向同一个L
def add_end(L = []):
L.append('END')
return L
print(add_end()) # ['END']
print(add_end()) # ['END', 'END'] 使用了上一次的默认参数
此外python还支持命名参数
# 支持命名参数
def test2(a, b):
print(a, b) # (1, 10)
test2(b=10, a=1)
# 通过字典接收额外传入的命名参数
def test2(a, b, **other):
print(a, b)
print(other) # 输出字典{'c': 100, 'd': 1000}
test2(b=10, a=1, c=100, d=1000)
与JavaScript一样,Python是按值传参的,
- 对于基础类型的参数而言,传递的是字面量
- 对于引用类型的参数而言,传递的是对象引用(内存地址)
函数的默认参数需要遵循下面规则
- 需要从右往左书写默认参数,否则无法被解析
- 默认值只被赋值一次,也就是说,如果默认参数是一个引用对象(比如数组),在后面的函数调用中都会在这一个对象上进行操作(而不是每个函数都具备独立的引用对象参数)
代码语句
赋值
Python支持连续赋值,且支持多重赋值
x = y = 1
a,b = 1,2
条件语句
Python是以缩进和冒号来表示语句块的,在条件语句中可以使用if...elif..else的语法
a = 9
if a < 10:
print("less")
elif a == 10:
print("equal")
else:
print("more")
循环语句
与其他语言类似,可以使用while语句
i = 0
while i < 10:
print(i*i)
python中不支持++操作符,因此使用for...in...循环有一些特殊的技巧
for i in range(1,10):
print(i)
变量作用域
函数的作用域:函数调用会为函数局部变量生成一个新的符号表。 确切地说,所有函数中的变量赋值都是将值存储在局部符号表。变量引用首先在局部符号表中查找,然后是包含函数的局部符号表,然后是全局符号表,最后是内置名字表。 因此,全局变量不能在函数中直接赋值(除非用 global 语句命名),尽管他们可以被引用。
a = 100
def test():
global a # 声明此处使用全局变量a
a = 10 # 对全局变量a进行修改
print(a) # 100
test()
print(a) # 10
在python3中增加了nonlocal
关键字
a = 100
def test():
a = 10 # 如果此处为定义a,则使用nonlocal会报错
def foo():
nonlocal a # 声明使用外层作用于的变量a
a = 20
print(a) # 10
foo()
print(a) # 20
print(a) # 100
test()
print(a) # 100
切片
切片是Python语法中一个比较有趣的地方。
a = "hello"
print(a[1:2])
- 切片是一个左闭右开区间
- 切片时的索引是在两个字符 之间 。左边第一个字符的索引为0,,而长度为 n 的字符串其最后一个字符的右界索引为 n,第三个参数可指定切片移动步长
字符串
Python的字符串不允许单独更改某个字符,但是可以使用灵活使用切片和字符串拼接获得新的字符串
a = "hello"
print(a[:(len(a)-1)] + "xx") # hellxx
print(a) # hello
字符串切片后,并未修改之前的字符串,而是返回了一个新的字符串
列表
列表与JS中的数组类似,其中的元素类型不必一致,可以通过下标获取列表元素的值,并可以直接对其进行修改。
对列表进行切片,返回的也是一个新的列表,对原列表没有影响。但是,如果是对切片进行赋值操作,则会修改原列表的值
a = [100,1,3,"ssds"]
a[:1] = ["xxx"]
print(a) # ['xxx', 1, 3, 'ssds']
需要注意的是,对列表切片进行赋值,该值必须是一个可以进行切片的值(比如字符串,列表),并且在赋值之前会对该值先进行切片,再赋值到原列表切片上
a = [100,1,3,"ssds"]
a[:1] = "xxx"
print(a) # ['x', 'x', 'x', 1, 3, 'ssds']
因此,如果直接为切片赋值一个数值是会报错的,因为数字不能直接切片
输入与输出
在print后加入一个逗号可以禁止输出换行
print "How old are you",
age = input()
# 等价于
age = input("how old are you")
获取程序参数
# 1.py
from sys import argv
script, first = argv
print(script)
print(first)
# 在运行时传入参数
python3 1.py 1
# 输出 1.py 1
类
参考
Python是一门面向对象的语言,如果了解其他面向对象的语言,则只需要掌握python一些特定的语法即可
class Test:
# 封装
x = 0 # 公有变量
__y = 100 # 私有变量,只能在当前类的方法中使用,无法在实例对象或子类中使用
# __init__为构造函数名称,在实例化对象时调用
def __init__(self, x):
print('hello init')
self.x = x
# 类方法第一个参数均为self
def hello(self):
print(self.x, self.__y)
t = Test(10)
t.hello()
# 继承
class Sub(Test):
z = 20
# 多态
def hello(self):
super(Sub, self).hello() # 调用父类的同名方法
print("sub hello")
def greet(self):
print(self.__y) # 报错,无法访问父类的私有属性
s = Sub(20)
s.hello()
除了__init__
之外,还有很多类的专有方法,如__add__
、__sub__
等等,可以用来实现运算符重载。
模块和包
参考
模块
模块是一个*.py
文件,一个模块对外暴露一个或多个接口。
我们通过例子来学习模块,首先新建一个mod1.py
文件
# mod1.py
def test():
print("hello test module")
然后在该文件同级目录新建测试文件main.py
import mod1
mod1.test() # 调用模块中的方法
# 可以替换模块别名
import mod1 as mod
mod.test()
此时的目录结构为
-- mod1.py
-- main.py
运行main.py
后会发现在当前目录自动生成了一个__pycache__
的目录,其内部包含一个mod1.cpython-37.pyc
的文件。import mod1
的本质是将mod1.py
文件中的所有代码加载到内存中,并在当前文件main.py
中声明并定义与模块同名的变量。
此外,还可以实现局部引入
# 导入模块的部分方法
from mod1 import test
test()
# 导入模块的全部方法
from mod1 import *
test() # 无需添加moduleName
引入其他目录的模块
接下来,在该级目录下新建一个目录module
,然后将mod1.py
文件移动至module
下,此时的目录结构为
-- module
-- mod1.py
-- main.py
然后重新运行代码,就会出现错误提示
ModuleNotFoundError: No module named 'mod1'
为了解决这个问题,我们首先需要了解python中的模块查找机制,在模块导入的时候
- 默认先在当前目录下查找
- 然后再在系统中查找,系统查找的范围是:
sys.path
下的所有路径,按顺序查找。
因此为了引入module/mod1.py
模块,我们可以通过sys.path
实现
import sys,os
# 追加sys.path
pwd = os.popen('pwd') # 获取当前main.py脚本运行的目录
dir = pwd.readline().strip()
sys.path.append(dir + '/module')
# 引入 ./module/ 目录下的模块
from mod1 import test
test()
通过pip install
安装第三方模块时,默认安装目录在Python安装路径/Lib/site-packages
下,该路径默认在sys.path
中,因此可以直接在代码中通过import
导入
包
在上面的例子中展示了通过修改sys.path
来引入其他目录模块的方式。除此之外,我们还可以通过包的形式来引入。
在module
目录下新增一个__init__.py
的文件,这个文件可以不用写任何东西,然后修改一下main.py
中的代码
from module import mod1
mod1.test() # 正常调用mod1模块中的方法
此时module
目录变成了一个包,导入包的本质就是执行该包下的__init__.py
文件,换言之,一个包可以理解为一个包含__init__.py
文件的目录。
在执行文件后会在module
目录下自动生成__pycache__
目录,然后就可以通过from packageName import moduleName
的形式引入包了。
包管理工具pip
除开python内置十分强大的标准库,丰富的第三方库也是python一个非常大的优势:大部分功能都可以在pypi的某个包中找到,而无需我们重新实现一遍。
与npm
类似,python使用pip
管理第三方库, Python 3 版本 >=3.4 开始已内置了pip。
requirements.txt
在多人合作开发等跨机器的项目中,我们需要保证该项目的依赖环境是一致的,与package.json
类似,python使用requirements.txt
来声明当前项目依赖的包列表,该文件可以通过下面命令生成
pip freeze > requirements.txt
然后将该文件提交到代码仓库中;就当在新的开发环境中拉取代码后,就可以通过下面命令安装依赖
cd 项目目录
pip install -r requirements.txt # 根据当前项目的requirements.txt安装依赖
venv虚拟环境
由于python的模块查找机制,pip安装的模块是全局安装的,这导致在同一个开发环境的两个python项目使用的依赖包都是安装在全局site-packages
下。
如果要想实现两个项目使用不同版本的包,则可以通过venv
搭建虚拟的python运行环境来实现,安装在虚拟环境中的包,不会对环境外的其他包造成影响。
# 创建虚拟环境
python3 -m venv venv # 会在当前目录新增一个venv的文件夹,里面保存着虚拟环境相关的数据
# 进入venv环境 bash模式
. venv/bin/activate
# 然后安装依赖
pip3 install xxx
# ...在虚拟环境执行其他操作
# 退出虚拟环境
deactivate
结合虚拟环境venv和依赖描述文件requirements.txt,我们就可以在虚拟环境中生成只与当前项目相关的描述文件,从而避免初始化项目依赖时污染全局模块。
小结
本文主要整理了Python的基本语法知识,用以备忘。
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。