前言:Python的语法比较简单,采用缩进方式,写出来的代码就像下面的样子:
# print absolute value of an integer:
a = 100
if a >= 0:
print(a)
else:
print(-a)
以#开头的语句是注释,注释是给人看的,可以是任意内容,解释器会忽略掉注释。其他每一行都是一个语句,当语句以冒号:结尾时,缩进的语句视为代码块。
缩进有利有弊。好处是强迫你写出格式化的代码,但没有规定缩进是几个空格还是Tab。按照约定俗成的惯例,应该始终坚持使用4个空格的缩进。
缩进的另一个好处是强迫你写出缩进较少的代码,你会倾向于把一段很长的代码拆分成若干函数,从而得到缩进较少的代码。
缩进的坏处就是“复制-粘贴”功能失效了,这是最坑爹的地方。当你重构代码时,粘贴过去的代码必须重新检查缩进是否正确。此外,IDE很难像格式化Java代码那样格式化Python代码。
最后,请务必注意,Python程序是大小写敏感的,如果写错了大小写,程序会报错。
- 数据类型和变量
- 整数:和数学写法一样,可以用_分隔过长的的数,如:10_000_000,0xa1b2_c3d4
- 浮点数:浮点数也叫小数,对于很大或者很小的浮点数必须用科学计数法,如:1.23e9,12.3e8,1.2e-5,整数运算永远是精确的(除法难道也是精确的?是的!),而浮点数运算则可能会有四舍五入的误差。
- 字符串:字符串可以用
''
或者""
括起来,如果字符串包含引号,可以使用转义字符\
,如'I\'m \"OK\"!'
展示的内容是I'm "OK"!
- 布尔值:
True、False
,布尔值可以用and、or、not
运算
(1) and:并且运算符,等同于js&&
例如:True &&False== true && false
(2) or:或运算符,等同于js||
例如:not True == !true
(3)not:取反值,等同于js!
例如:not True == !true
- 空值:空值用
None
表示,同jsnull
- 变量:变量名必须是大小写英文、数字和_的组合,且不能用数字开头,
声明变量直接变量名=变量值
,例如:a=1
,python变量如js不固定类型,可以赋值给任意类型 - 常量:python通常用全大写的变量名表示常量;例:
PI = 3.1415926
,但实际上PI
依然是个变量,Python没有任何机制保证一个常量,用大写变量名只是约定俗成 - 运算:除法通常用
/
或者//
/
除法计算结果是浮点值,也就是会把小数部分计算出来
10 / 3 # 3.3333333333333335
//
除法计算结果一定是整数,称为地板除,会舍弃余数,余数运算符和js同,%
10 // 3 # 3
10 % 3 # 1
- 集合(set):表示无序且元素唯一的集合,用花括号
{}
或者set()
函数创建,例如:{1, 2, 3}
或set([1, 2, 3])
。
- 编码
python提供了ord()
函数来获取字符的整数表示,chr()
函数把Unicode编码转换为对应的字符
ord('A') # 65
ord('好') # 22909
chr(66) # 'B'
chr(20154) # '人'
'\u4e2d\u6587' == '中文'
由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。Python对bytes类型的数据用带b前缀的单引号或双引号表示,以Unicode表示的str通过encode()方法可以编码为指定的bytes,例如:
'ABC'.encode('ascii')
b'ABC'
'中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
'中文'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
字符串方法len()
,计算的是str的字符数,如果换成bytes
,len()
计算的就是字节数
len('abc') # 3
len('中国') # 2
len(b'ABC') # 3 //字符串前面带b表示转为字节
len(b'\xe4\xb8\xad\xe6\x96\x87') # 6
len('中国'.encode('utf-8')) # 6
%
在python用来格式化字符串,%s
表示用字符串替换,有几个%
占位符,后面就跟几个变量或者值,顺序要对应,只有一个的可以省略括号
占位符 替换内容
%d => 整数
%f => 浮点数
%s => 字符串
%x => 十六进制整数
例:
'Hello, %s' % 'world' # 'hello, world'
'Hi, %s, you have $%d.' % ('Michael', 1000000) #'Hi, Michael, you have $1000000.'
print('%2d-%02d' % (3, 1)) # 3-01 //补0
print('%.2f' % 3.1415926) #3.14 //指定小数的位数
'Age: %s. Gender: %s' % (25, True) #'Age: 25. Gender: True' //如果不确定类型,%s永远会将任何类型转为字符串
'growth rate: %d %%' % 7 #'growth rate: 7 %' //如果要显示%字符,用%%表示
format()
format()
方法也可以用来格式化字符串,他会使用传入的参数依次替换字符串内的占位符{0}
,{`}
......
'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125) #Hello, 小明, 成绩提升了 17.1%'
f-string
最后一种格式化字符串的方法是使用以f开头的字符串,称之为f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换:(类似于jsx模板字符串
)
>>> r = 2.5
>>> s = 3.14 * r ** 2
>>> print(f'The area of a circle with radius {r} is {s:.2f}')
#The area of a circle with radius 2.5 is 19.62
#上述代码中,{r}被变量r的值替换,{s:.2f}被变量s的值替换,并且:后面的.2f指定了格式化参数(即保留两位小数),因此,{s:.2f}的替换结果是19.62。
list
python内置的一种数据类型,列表:list
,list是一种有序集合,可以随时添加删除元素,例如,下面就是i一个简单的list:
name = ['tom','jerry','maker']
len(name) #3 //和字符串一样,len()方法可以获得list的元素个数
name[0] #tom
name[4]
#Traceback (most recent call last):
#File "<stdin>", line 1, in <module>
#IndexError: list index out of range
name[1] #jerry //如果需要访问list的某个元素可以使用索引访问,和js类似,当索引超出范围python会报错
name[-1] #maker //使用-1作为索引可以取出最后一个元素,而不用使用 len(name) -1来计算索引位置,同理倒数第2、3个一样
数组方法介绍:
append() // 向list末尾添加一个元素
name.append('jack') #['tom','jerry','maker','jack']
insert() //把元素插入list到指定位置,第一个参数为索引,第二个参数为元素
name.insert(1,'bod') # ['tom','bod','jerry','maker','jack']
pop() //删除list末尾的元素,会返回删除的元素,如果要删除指定位置的元素,需要给pop传入一个索引
name.pop() #['tom','bod','jerry','maker']
name.pop(1) #['tom','jerry','maker']
list可以有不同类型的数据,亦可以多层嵌套,如果没有元素的list,len()长度为0
tuple
另一种有序列表叫元组:tuple
。tuple
和list
非常类似,但是tuple
一旦初始化就不能修改,比如同样是列出同学的名字:
name = ('top','jerry','maker')
name
这个tuple
创建之后酒不能变了,它也没有append()、pop()这样的方法,其他使用方法和list
一样,但是不能赋值。
不可变的tuple
有什么意义?因为tuple
不可变,所以代码更安全。如果可能,能用tuple
代替list
就尽量用tuple
。
tuple
的陷阱,由于tuple
使用小括号定义,而小括号本身是数学公式的小括号的意思,所以我们的tupe
如果只有一个元素,python默认认为他是一个数学公式,所以,我们需要在元素末尾加上,
例如:
a = (1) # 1
a= (1,) # (1,)
那么tuple是不是一定不能修改呢,其实不是的,如果内部元素的引用地址不变的化是可以改变的,例如:
name = ('a',['b','c'],'d')
name[1][0] = 'f' #//('a',['f','c'],'d')
条件判断if else
a = 1
if a >= 0:
print('正数')
else:
print('负数')
if a > 0:
print('正数')
elif a == 0:
print('0')
else:
print('负数')
ps:如果我们定义一个input
让用户输入,需要注意input
收集的值是str
类型,如果要和int
类型比较的话需要先用int()
转成int
类型,如果int()
方法接收的参数不是一个字符串数字,则会报错
s = input('age:')
age = int(s)
if age > 18:
print('成年')
else:
print('未成年')
模式匹配 match case
如果我们的条件判断过于多的话,if else
的嵌套不利于阅读,这个时候就能用到我们的match case
,类似于js的switch
语句,python的case
匹配是可以匹配多个值和一定范围的。
age = 15
match age:
case x if x < 10:
print(f'< 10 years old: {x}')
case 10:
print('10 years old.')
case 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18:
print('11~18 years old.')
case 19:
print('19 years old.')
case _: #_表示匹配除了条件外的情况
print('not sure.')
循环 for in
、while
sum = 0
for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
sum = sum + x
print(sum)
sum = 0
for x in range(101): #range可以生成一个整数序列,再通过list()转换为list,range(100)生成从0开始100个整数也就是0-99
sum = sum + x
print(sum)
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
break # break语句会立即结束当前循环
continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行
print(sum) #while 循环只要条件成立就一直会循环下去
对象字典
obj = {'name':'tom','age':18} #创建字典
'name' in obj #判断key是否存在在对象
obj.get('name',-1) # 判断key是否存在在对象,第二个参数是自定义没有的结果返回值
obj.pop('name') #删除一个key,
在Python中,list
、tuple
、dict
和set
是四种常见的数据类型,它们各自有不同的特点和用途。
list
(列表)是一种有序的可变序列,可以存储任意类型的元素。列表使用方括号[]
来表示,元素之间用逗号,
分隔。列表支持索引、切片、添加、删除、修改等操作,是Python中最常用的数据类型之一。tuple
(元组)是一种有序的不可变序列,可以存储任意类型的元素。元组使用圆括号()
来表示,元素之间用逗号,
分隔。元组支持索引、切片等操作,但不支持添加、删除、修改等操作。元组通常用于存储不可变的数据,如坐标、颜色等。dict
(字典)是一种无序的键值对集合,可以存储任意类型的键和值。字典使用花括号{}
来表示,每个键值对之间用冒号:
分隔,键值对之间用逗号,
分隔。字典支持通过键来访问值,也支持添加、删除、修改等操作。字典通常用于存储具有映射关系的数据,如姓名和电话号码的对应关系。set
(集合)是一种无序的元素集合,可以存储任意类型的元素。集合使用花括号{}
来表示,元素之间用逗号,
分隔。集合支持添加、删除、交集、并集、差集等操作。集合通常用于去重、交集、并集等操作。
需要注意的是,list
、tuple
、dict
和set
是不同的数据类型,它们之间不能直接进行转换。如果需要将它们之间进行转换,需要使用相应的转换函数,如list()
、tuple()
、dict()
和set()
。
函数
- 函数定义使用
def
语句,依次写出函数名、括号、括号中的入参和冒号:
,然后在缩进块中编写函数体,返回值用return
返回,例:
def sum(x,y):
return x + y
sum(2,3) # 5
- 函数的导入:
from 文件名 import 函数名
- 如果想让写一个什么都不做的函数或者
if
语句里什么都不做,并且让代码跑起来,可以使用pass
占位符
def nop():
pass
if age > 18:
pass
-
isinstance()
内置函数 可以用来检查参数类型,raise TypeError()
可以抛出一个错误
def my_abs(x):
if not isinstance(x, (int, float)): #如果参数不是整数和浮点数,则抛出错误
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
列表生成式
在python
中,如果想要快速生成一个长度若干的list
,python
内置了非常强大的list
生成式。
例子:如果要生成一个[1,2,3,4,5,6,7,8,9]
,可以用list(range(1,10))
,如果要生成[1x1,2x2,3x3,....,10x10],除了使用循环,还能用更便捷的列表生成式。
#循环
L = []
for x in range(1,11):
L.append(x*x)
print(L) #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
#list生成式
[x * x for x in range(1,11)] # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
可以看到列表生成式比循环生成要简便很多,for
循环后面还能跟上if
判断,这样更方便筛选出符合条件的值,例:
[x * x for in range(1,11) if x % 2 == 0] #[4, 16, 36, 64, 100] , 筛选出偶数的平方
[m + n for m in 'ABC' for in 'XYZ'] #双重循环 ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
d = {'x': 'A', 'y': 'B', 'z': 'C' }
[k + '=' + v for k, v in d.items()] # 多个变量循环 ['y=B', 'x=A', 'z=C']
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python
中,这种一边循环一边计算的机制,称为生成器:generator
。
要创建一个generator
,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator
:
L = [x * x for x in range(10)] # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x * x for x in range(10)) #<generator object <genexpr> at 0x1022ef630>
创建L和g的区别仅在于最外层的[]
和()
,L
是一个list
,而g
是一个generator
。
我们可以直接打印出list
的每一个元素,但我们怎么打印出generator
的每一个元素呢?
如果要一个一个打印出来,可以通过next()
函数获得generator
的下一个返回值:
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration```
我们讲过,generator
保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
当然,上面这种不断调用next(g)
实在是太变态了,正确的方法是使用for
循环,因为generator
也是可迭代对象:
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
所以,我们创建了一个generator
后,基本上永远不会调用next()
,而是通过for
循环来迭代它,并且不需要关心StopIteration
的错误。
generator
非常强大。如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator
。
也就是说,上面的函数和generator
仅一步之遥。要把fib
函数变成generator
函数,只需要把print(b)
改为yield b
就可以了:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
这就是定义generator
的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator
函数,调用一个generator
函数将返回一个generator
:
f = fib(6) #<generator object fib at 0x104feaaa0>
高阶函数
-
map
reduce
map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回
def f(x):
return x * x
r = map(f,[1,2,3,4,5,6,7,8,9])
list(r) # [1,4,9,16,25,36,49,64,81]
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算。
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> def char2num(s):
... digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
... return digits[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579
filter
过滤
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
sorted
排序
sorted([36,5,-12,9,-21]) #[-21,-12,5,9,36]
sorted(([36,5,-12,9,-21],key=abs) # 按绝对值大小排序 abs() 获取绝对值,[5, 9, -12, -21, 36]
- 装饰器
我们有一个函数now()
,现在,假设我们要增强now()
函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()
函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
。
本质上,decorator
就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator
,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
观察上面的log,因为它是一个decorator
,所以接受一个函数作为参数,并返回一个函数。我们要借助Python
的@
语法,把decorator
置于函数的定义处。把@log
放到now()
函数的定义处,相当于执行了语句:now = log(now)
,由于log()
是一个decorator
,返回一个函数,所以,原来的now()
函数仍然存在,只是现在同名的now
变量指向了新的函数,于是调用now()
将执行新函数,即在log()
函数中返回的wrapper()
函数。
- 偏函数
Python
的functools
模块提供了很多有用的功能,其中一个就是偏函数(Partial function)
。要注意,这里的偏函数和数学意义上的偏函数不一样。
在介绍函数参数的时候,我们讲到,通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。举例如下:
int('12345') #12345
int('12345',base=8) # 转成8进制 5439
int('12345',16) #转成16进制 74565
def int2(x,base=2):
return int(x,base)
int2('1000000') # 函数默认base传2 64
int()
函数可以把字符串转换为整数,当仅传入字符串时,int()
函数默认按十进制转换,但int()
函数还提供额外的base
参数,默认值为10
。如果传入base
参数,就可以做N进制的转换,假设要转换大量的二进制字符串
,每次都传入int(x, base=2)
非常麻烦,于是,我们想到,可以定义一个int2()
的函数,默认把base=2
传进去,functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2()
,可以直接使用下面的代码创建一个新的函数int2
,
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
当函数的参数个数太多,需要简化时,使用functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单
类和实例
python
中使用class
关键字来定义类,class
后面紧跟类名,类名通常首字母大写,再后面是表示从哪个类上面继承下来的
class Student(object): #(object) 如果没有继承类就是继承(object)类,所有类最终都会继承(object)类
pass
python
实例是通过调用类来实现的也就是类名+()
:
bart = Student() #通过Student类创建bart实例
当我们想快速创建相同模板的实例,可以通过给类强制填写某些属性或者方法来实现,__init__
方法,相当于js
的constructor
函数,可以在里边定义你想要的属性,__init__
方法自带self
入参,相当于this
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
def get_age(self): #自定义实例方法
if self.age >= 18:
return '成年'
else:
return '未成年'
bart = Student('Tom',26) #通过类创建实例,传入初始属性name和age
bart.name bart.age # Tom 26
bart.get_age() #成年
bart.age = 16
bart.name bart.age # Tom 16
bart.get_age() #未成年