Python基础语法

134 阅读19分钟

PythonJavaScriptPHPShell等都是解释型语言,可以边执行边转换,不会生成可执行程序。与之对应的还有编译型语言,需要编译器编译源代码为可执行文件,再去执行。编译型语言的运行由于操作系统架构的差异,通常不能跨平台。比如:Windows程序不能直接运行到macOS。解释型语言因解释器(官方针对不同平台开发的不同的解释器)的存在,可以做到一次编写,到处运行

历史

  • Python 是是荷兰人 Guido van Rossum (吉多·范罗苏姆)1990 年初开发的。
  • 1991Python 第一个公开发行版问世。
  • 2004 年,Python 的使用率呈线性增长,不断受到编程者的欢迎和喜爱;
  • 2010 年,Python 荣膺 TIOBE 『2010 年度最佳编程语言』桂冠;
  • 2017 年,IEEE Spectrum 发布的 『2017 年度编程语言』排行榜中,Python 位居第 1 位;
  • 2018 年,Python 斩获 TIOBE『2018 年度最佳编程语言』第 1 名;
  • 2020202120222023Python 更是连续四年摘得 TIOBE『年度最佳编程语言』桂冠。

优缺点

服务领域

  • 开发web应用
  • 自动化脚本编写
  • 网络爬虫
  • ...
  • 人工智能:目前世界上优秀的人工智能学习框架,比如 Google 的 TransorFlow(神经网络框架)、FaceBook 的 PyTorch(神经网络框架)以及开源社区的 Karas 神经网络库等,都是用 Python 实现的。

语法差异

三目运算符

Python实现三目运算的格式:exp1 if contion else exp2

if a>b:
    max = a;
else:
    max = b;
# 三目运算简写上述代码
max = a if a > b else b
# 支持嵌套
max = a if a > b else (c if c > d else d)

字符串

  1. 特殊字符的转义\,取消转义r
# 中间换行
 s ='First line.\nSecond line.' 
# 取消转义, 一行输出
s = r'First line.\nSecond line.'
  1. 字符串可以用 + 合并(粘到一起),也可以用 * 重复
# 3 times 'un', followed by 'ium'
3 * 'un' + 'ium' # 'unununium'
  1. 字符串合并
# 1.相邻的两个或多个 *字符串字面值* (引号标注的字符)会自动合并
'Py' 'thon' # => 'Python'
# 2.长字符串分隔&合并: 合并输出
text = ('Put several strings within parentheses '
        'to have them joined together.') 

上述方式只适用于两个字面值,不能用于变量或表达式,合并多个变量,或合并变量与字面值,要用 +

a = 'x'
a 'y' # 错误
('x' * 3) 'y' # 错误
a + 'y' # 正确
('x' * 3) + 'y' #正确 
  1. 字符串的索引、切片访问

索引支持负数,用负数索引时,从右边开始计数

word = 'freedom'
word[0]  #f
word[-1] #m

切片,获取子字符串

# 2-5之间的子字符串,含2不含5
word[2:5]
#省略开始索引时,默认值为 0,省略结束索引时,默认为到字符串的结尾
word[:2]
word[4:]
word[-2:] # 倒数第二位到结束:'om'

输出结果包含切片开始,但不包含切片结束。因此,s[:i] + s[i:] 总是等于 s。 索引越界会报错,切片会自动处理越界索引。 Python字符串是不可修改的,不能为字符串中某个索引位置赋值。

格式化字符串

# 字符串开头的引号/三引号前添加 `f` 或 `F`
event = 'Referendum'
print(f'Results of the {year} {event}') #'Results of the 2016 Referendum'
# str() | repr() 输出
s = 'Hello, world.'
ss = 'python'
print('输出:' + repr(ss) + ' ' + str(s)) #输出:'python' Hello, world.
# 在 ':' 后传递整数,为该字段设置最小字符宽度,常用于列对齐:
table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
for name, phone in table.items():
    #`:d`表示这是一个整数格式化指示符,用于将变量格式化为整数
    print(f'{name:10} ==> {phone:10d}')
#Sjoerd     ==>       4127
#Jack       ==>       4098
#Dcab       ==>       7678

字符串format()方法

#We are the knights who say "Ni!"
print('We are the {} who say "{}!"'.format('knights', 'Ni'))
#spam and eggs
print('{0} and {1}'.format('spam', 'eggs'))
#eggs and spam
print('{1} and {0}'.format('spam', 'eggs'))
#This spam is absolutely horrible.
print('This {food} is {adjective}.'.format(
      food='spam', adjective='absolutely horrible'))
# The story of Bill, Manfred, and Georg.
print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
                                                   other='Georg'))
                                                   
table1 = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
table2 = {'Alice': 1234, 'Bob': 5678}
# 0 fomat参数位置,方括号 '[]' 访问键,`:d`表示这是一个整数格式化指示符,用于将变量格式化为整数
print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
      'Dcab: {0[Dcab]:d}'.format(table1))
# 1 fomat参数位置,方括号 '[]' 访问键,`:d`表示这是一个整数格式化指示符,用于将变量格式化为整数
print('Alice: {1[Alice]:d}; Bob: {1[Bob]:d}'.format(table1, table2)) 
#也可以通过将 `table` 字典作为采用 `**` 标记的关键字参数传入来实现。
print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table1))

列表

列表和字符串有很多共性,支持索引与切片,并且是可变的,支持索引与切片修改。详见

fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
fruits.count('apple') # 2
fruits.count('tangerine') #0
fruits.index('banana') #3

fruits.index('banana', 4)  # Find next banana starting at position 4
fruits.reverse() #翻转
fruits.append('grape') #添加
fruits.sort() #排序
fruits.pop() #移除末尾元素

元组

元组由多个用逗号隔开的值组成。输出时,元组都要由圆括号标注,这样才能正确地解释嵌套元组。输入时,圆括号可有可无,不过经常是必须的。元组是不可变的

t = 12345, 54321, 'hello!'
t[0]

# Tuples may be nested:
u = t, (1, 2, 3, 4, 5)

# Tuples are immutable:
t[0] = 88888

# but they can contain mutable objects:
v = ([1, 2, 3], [3, 2, 1])

集合

创建集合用花括号或 set() 函数。注意,创建空集合只能用 set(),不能用 {}{} 创建的是空字典。

basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
print(basket) # show that duplicates have been removed
 # fast membership testing
'orange' in basket                

# Demonstrate set operations on unique letters from two words
a = set('abracadabra')
b = set('alacazam')
a - b                              # letters in a but not in b
a | b                              # letters in a or b or both
a & b                              # letters in both a and b
a ^ b                              # letters in a or b but not both

字典

字典是以  进行索引的,键可以是任何不可变类型;字符串和数字总是可以作为键。 如果一个元组只包含字符串、数字或元组则也可以作为键;如果一个元组直接或间接地包含了任何可变对象,则不能作为键。 列表不能作为键,因为列表可以使用索引赋值、切片赋值或者 append() 和 extend() 等方法进行原地修改列表。

对字典执行 list(d) 操作,返回该字典中所有键的列表,按插入次序排列(如需排序,请使用 sorted(d))。检查字典里是否存在某个键,使用关键字 in

tel = {'jack': 4098, 'sape': 4139}
tel['guido'] = 4127
tel['jack'] # 4098

del tel['sape'] #删除了key为`sape`的键值对
list(tel) #['jack', 'guido']
sorted(tel) #['guido','jack']
'guido' in tel # True
'jack' not in tel # False

特殊的创建方式

#用键值对序列创建字典
dic = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
#关键字是比较简单的字符串时,用关键字参数指定键值对
dic = dict(sape=4139, guido=4127, jack=4098)

序列推导式

对序列或可迭代对象中的每个元素应用某种操作,用生成的结果创建新的列表;

列表

示例:创建平方值的列表

nums = []
for i in range(10):
    nums.append(i ** 2)
print(nums,i) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 9

变量 i,该变量在循环结束后仍然存在。 有两种方式可以避免这种问题:

nums = list(map(lambda x: x ** 2, range(10)))
# 等价于列表推导式:
nums = [x**2 for x in range(10)]
# 嵌套 for
vec = [[1,2,3], [4,5,6], [7,8,9]]
nums =[num for elem in vec for num in elem]
# 嵌套if
nums = [x**2 for x in range(10) if x % 2 == 0]

可推导的其他序列

#集合
a = {x for x in 'abracadabra' if x not in 'abc'}
#字典
dic = {x: x**2 for x in (2, 4, 6)}

序列循环

对字典执行循环时,可以使用 items()方法同时提取键及其对应的值。

knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
    print(k, v)

序列中循环时,用 enumerate()函数可以同时取出位置索引和对应的值:

for i, v in enumerate(['tic', 'tac', 'toe']):
    print(i, v)

同时循环两个或多个序列时,用 zip() 函数可以将其内的元素一一匹配:

questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):
    print('What is your {0}?  It is {1}.'.format(q, a))

del语句

有一种方式可以按索引而不是值从列表中移除条目: del语句

a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0]#删除索引0的值
del a[2:4]#删除索引2~4的值,含4不含2
del a[:] # 删除所有
del a #删除整个变量

用 del 可以删除键值对

tel = {'jack': 4098, 'sape': 4139} 
del tel['sape'] #删除了key为`sape`的键值对

控制流

if...elif..elsefor ... inwhilebreakcontinue... 与其他语言一样。

match语句 接受一个表达式并把它的值与一个或多个 case 块给出的一系列模式进行比较。

# point is an (x, y) tuple
match point:
    case (0, 0):
        print("Origin")
    case (0, y):
        print(f"Y={y}")
    case (x, 0):
        print(f"X={x}")
    case (x, y):
        print(f"X={x}, Y={y}")
    case _:
        raise ValueError("Not a point")

定义函数

定义函数使用关键字 def,后跟函数名与括号内的形参列表。函数语句从下一行开始,并且必须缩进。

def sum(a, b):
    return a + b
# 参数默认值
def sum(a, b = 3):
    return a + b

函数的参数传值方式有两种:参数位置,参数关键字

# 位置参数
sum(1,2)
# 关键字参数
sum(a=3,b=2)
sum(b=2,a=3)
# 参数默认值
sum(2)
sum(a=2)

注意: 参数默认值为列表、字典或类实例等可变对象时,默认值会使用多次。

def f(a, L=[]):
    L.append(a)
    return L
print(f(1))
print(f(2))
print(f(3))
#输出
[1]
[1, 2]
[1, 2, 3]

不想在后续调用之间共享默认值时,应以如下方式编写函数:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

特殊参数 默认情况下,参数可以按位置或显式关键字传递给 Python 函数。为了让代码易读、高效,最好限制参数的传递方式,这样,开发者只需查看函数定义,即可确定参数项是仅按位置、按位置或关键字,还是仅按关键字传递。

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |             |                  |
        |        Positional or keyword   |
        |                                - Keyword only
         -- Positional only

/ 和 * 是可选的。这些符号表明形参如何把参数值传递给函数:位置、位置或关键字、关键字。关键字形参也叫作命名形参。

函数定义中未使用 / 和 * 时,参数可以按位置或关键字传递给函数。 仅限位置形参放在 / (正斜杠)前。/ 用于在逻辑上分割仅限位置形参与其它形参。/ 后可以是 位置或关键字 或 仅限关键字 形参。

把形参标记为 仅限关键字,表明必须以关键字参数形式传递该形参,应在参数列表中第一个 仅限关键字 形参前添加 *

# `/`前面的形参使用位置参数
def sum(a,/,b = 3):
    return a + b
# 支持的调用方式
c = sum(2,b=3)
c = sum(2,4)

#`*`后面的参数使用关键字参数
def sum(a,*,b = 3):
    return a + b
# 支持调用
c = sum(2,b=3)
c = sum(a=2,b=4)

参数解包 函数调用要求独立的位置参数,但实参在列表或元组里时,要执行相反的操作。用 * 操作符把实参从列表或元组解包出来:

def sum(a,b = 3,/):
    return a + b
array = [3, 4]
c = sum(*array) # 7

同样,字典可以用 ** 操作符传递关键字参数

def sum(*, a,b = 3):
    return a + b
dic = {"a": 4, "b": 5}
c = sum(**dic) # 9

Lambda 表达式 关键字用于创建小巧的匿名函数。lambda a, b: a+b 函数返回两个参数的和。Lambda 函数可用于任何需要函数对象的地方。在语法上,匿名函数只能是单个表达式。在语义上,它只是常规函数定义的语法糖。与嵌套函数定义一样,lambda 函数可以引用包含作用域中的变量:

# 返回 Lambda
def make_incrementor(n):
    return lambda x: x + n

f = make_incrementor(42)
print(f(10)) #52

模块

新建sum.py文件,这个文件就是 模块,模块中的定义可以导入到其他模块。在模块内部,通过全局变量 __name__ 可以获取模块名(即字符串)

# sum.py
def add3(*, a : int ,b = 3):
    return a + b

使用import sumhello.py执行导入, 此操作不会直接把 sum 中定义的函数名称添加到当前命名空间,只是添加模块的名称,然后通过名称去访问其中的函数。

import sum
res = sum.add3(a=10)
print(res) #13

每个模块都有自己的私有命名空间,它会被用作模块中定义的所有函数的全局命名空间。可以在模块内使用全局变量而不必担心与用户的全局变量发生意外冲突;可以通过sum.someVar去访问模块内部的变量。

导入方式的变体

# sum 模块内的定义的 add3 导入

from sum import add3 
res = add3(a=10)
print(res) #13

# sum 模块内的定义所有变量,函数导入, 这种方式会导入所有不以下划线(`_`)开头的名称
from sum import * 
res = add3(a=10)
print(res) #13

# 模块命名
import sum as moduleSum
res = moduleSum.add3(a=10)
print(res) #13

# 导入的函数、变量命名
from sum import add3 as addThree
res = addThree(a=10)
print(res) #13

脚本执行模块

# 运行
python sum.py <arguments>

这项操作将执行模块里的代码,和导入模块一样,但会把 __name__ 赋值为 "__main__",所以需要把如下代码添加到模块的末尾;

def add3(a ,b = 3):
    return a + b
# 脚本执行。
if __name__ == "__main__":
    import sys
    print(add3(int(sys.argv[1])))

内置函数 dir() 用于查找模块定义的函数,变量。返回结果是经过排序的字符串列表:

import sum
res = sum.add3(a=10)
print(dir(sum))
#['__builtins__', '__cached__', '__doc__', 
# '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add3']

没有参数时,dir() 列出当前已定义的变量、函数。

读写文件

以特定模式和编码打开文件,返回一个文件对象,基于此文件对象进行读写操作。

open(file, mode='r' , buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

实参操作文件的模式有:

modedescription
'r'读取(默认),与 'rt' 同义
'w'写入
'x'排它性创建,如果文件已存在则失败
'a'打开文件用于写入,如果文件存在则在末尾追加
'b'二进制模式
't'文本模式(默认)
'+'打开用于更新(读取与写入)

在处理文件对象时,最好使用 with 关键字。优点是,子句体结束后,文件会正确关闭,即便触发异常也可以。


with open('record.txt','r', encoding='utf-8') as file:
  # print(file.read())
  # file.write('李白\n')
  # 逐行读取
  for line in file:
      print(line)

如果没有使用 with关键字,则应调用 file.close() 关闭文件,即可释放文件占用的系统资源。

调用 f.write() 时,未使用 with 关键字,或未调用 f.close(),即使程序正常退出,也可能导致 f.write() 的参数没有完全写入磁盘。

命名空间

Python 一般有三种:

  • 内置名称Python内置的名称,比如函数名 abschar 和异常名 Exception等。
  • 全局名称 : 模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
  • 局部名称 :函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。
# var1 是全局名称
var1 = 5
def some_func(): 
    # var2 是局部名称
    var2 = 6
    def some_inner_func(): 
        # var3 是内嵌的局部名称
        var3 = 7

作用域

Python中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。

python的作用域一共有4种:

  • Local :最内层,包含局部变量,比如一个函数/方法内部。
  • Enclosing :包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal
  • Global :当前脚本的最外层,比如当前模块的全局变量。
  • Built-in : 包含了内置的变量/关键字等。
# 内置的变量/关键字       
import builtins
print(dir(builtins))

g_count = 0  # 全局作用域
def outer():
    o_count = 1  # 闭包函数外的函数中
    def inner():
        i_count = 2  # 局部作用域

Python 中只有模块(module),类(class)以及函数(deflambda)才会引入新的作用域,其它的代码块(如 if/elif/else/try/exceptfor/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,如下代码:

import os
if not os.path.exists('./path/demo'):
    message = 'dir not found'
print(message) # dir not found

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。 调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。

total = 0 # 这是一个全局变量
def sum( arg1, arg2 ):
    # UnboundLocalError: local variable 'total' referenced before assignment
    # print ("函数内范文全部变量会异常 : ", total)
    #返回2个参数的和."
    total = arg1 + arg2 # total在这里是局部变量.
    print ("函数内是局部变量 : ", total)  # 30
    return total
 
#调用sum函数
sum( 10, 20 ) # 30
print ("函数外是全局变量 : ", total) # 0

# 输出结果
# 函数内是局部变量 :  30
# 函数外是全局变量 :  0

global 和 nonlocal

当内部作用域想修改外部作用域的变量时,就要用到 globalnonlocal 关键字。

作用域内修改全局变量:

total = 0 # 这是一个全局变量
def sum( arg1, arg2 ):
    global total
    total = arg1 + arg2 
    print ("函数内修改全局变量 : ", total)  # 30
    return total
 
sum( 10, 20 ) # 30
print ("函数外输出修改后的全局变量: ", total) # 30

修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量:

def outer():
    x = 10
    def inner():
        nonlocal x # nonlocal关键字声明
        x = 20
        print(x)
    inner()
    print(x)
outer()
#输出
#20
#20

Python 的类支持面向对象编程(OOP)的所有标准特性:类的继承机制支持多个基类、派生的类能覆盖基类的方法、类的方法能调用基类中的同名方法。对象可包含任意数量和类型的数据。和模块一样,类也支持 Python 动态特性:在运行时创建,创建后还可以修改。

Python类属性与方法的特殊约定:

  • __private_attrs :两个下划线开头,声明该属性为类的私有属性,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs

  • 在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类的方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。 self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定使用 self

  • __private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 self.__private_methods ,不能在类的外部调用。

类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用。__init__() 方法可以有参数,参数通过 __init__() 传递到类的实例化操作上。

class people: 
    # 定义基本属性
    name = ''
    age = 0
    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    #定义类的方法
    def speak(self):
        print(f"个人介绍:\n{self.__info()}")
    #定义类的私有方法
    def __info(self): 
        return "name:%s,age:%d,weight:%d" %(self.name,self.age,self.__weight)

#实例化
p = people('小花',20,130)
p.speak() #个人介绍:\n  name:小花,age:20,weight:130
#print(p.__weight) #AttributeError: 'people' object has no attribute '__weight'
#print(p.__info()) #AttributeError: 'people' object has no attribute '__info'

类的继承

# 多继承语法 class student(people,...):
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        # 调用父类的构函
        people.__init__(self,n,a,w)
        self.grade = g
    # 覆写父类的方法
    def speak(self):
        print(f"学生介绍:\n{self.__info()}")
    # 父类的私有属性未被继承,不能使用:self.__weight
    def __info(self): 
        return "name:%s,age:%d,grade:%d" %(self.name,self.age,self.grade)
    
stu = student('李四', 10, 60, 85)
stu.speak() # 学生介绍:\n name:李四,age:10,grade:85
#用子类对象调用父类已被覆盖的方法
super(student,stu).speak() #个人介绍:\n name:李四,age:10,weight:60

类的专有方法除了__init__还有 __del____hash____repr____add__等。

标准库

标准库是 Python 编程语言的一部分,它包含了大量的内置模块和函数,可用于各种常见的编程任务,例如文件操作、网络通信、数据处理、文本处理、数学计算、日期时间处理等。 标准库中的一些常用模块:

  • os 模块提供了许多与操作系统交互的函数
    import os
    files_and_dirs = os.listdir(os.getcwd()) # 列出指定目录下的文件和子目录
    os.mkdir(os.path.join(os.getcwd(), 'test')) #当前工作目录下创建test目录
    
  • sys 模块提供了与 Python 解释器和系统相关的功能
    if __name__ == "__main__":
     import sys
     print(add3(int(sys.argv[1]))) # 获取命令行参数并执行函数
    
  • time 模块提供了处理时间的函数
    import datetime
    # 获取当前日期和时间
    current_datetime = datetime.datetime.now()
    
  • math 模块提供了数学函数
    import math
    angle = math.radians(30)
    print(math.sin(angle)) #正弦值计算
    
  • re 模块提供了正则表达式处理函数
      import re
      # 查找匹配的字符串
      text = "Hello, world! This is a test string."
      pattern = r"world"
      match = re.search(pattern, text)
      if match:
         #找到匹配的字符串,替换未`Python`
         new_text = re.sub(pattern, "Python", text)
         print(new_text)
    
  • json 模块提供了 JSON 编码和解码函数
    import json
    # 将字典对象编码为 JSON 格式字符串
    data = {"name": "John", "age": 30, "city": "New York"}
    json_string = json.dumps(data)
    print("编码后的 JSON 字符串:", json_string)

    # 将 JSON 格式字符串解码为字典对象
    decoded_data = json.loads(json_string)
    print("解码后的字典对象:", decoded_data)
  • urllib 模块提供了访问网页和处理 URL 的功能
     from urllib.request import urlopen
     with urlopen('http://worldtimeapi.org/api/timezone/etc/UTC.txt') as response:
         for line in response:
             line = line.decode()             # Convert bytes to a str
             if line.startswith('datetime'):
                 print(line.rstrip())         # Remove trailing newline