返回
Python是著名的“龟叔”Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言。
C语言是可以用来编写操作系统的贴近硬件的语言,所以,C语言适合开发那些追求运行速度、充分发挥硬件性能的程序。而Python是用来编写应用程序的高级编程语言。 总的来说,Python的哲学就是简单优雅,尽量写容易看明白的代码,尽量写少的代码。
python缺点:
Python支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来。
因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4个字节可以表示的最大整数是4294967295。
Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。 Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。
现在,捋一捋ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。
字母A用ASCII编码是十进制的65,二进制的01000001;
字符0用ASCII编码是十进制的48,二进制的00110000,注意字符'0'和整数0是不同的;
汉字中已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。
你可以猜测,如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001。
新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。
所以,本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
中 | x | 01001110 00101101 | 11100100 10111000 10101101 |
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
print("I am \"OK\"!")
print('\\\t\\')
print(r'\\\t\\')
print('''line1
line2
line3''')
# I am "OK"!
# \ \
# \\\t\\
# line1
# line2
# line3
#字符和字符串
print('包含中文的str')
print(ord('A'))
print(ord('中'))
print(chr(66))
print(chr(25991))
print('\u4e2d\u6587')
# 包含中文的str
# 65
# 20013
# B
# 文
# 中文
#字符串和字节
print('ABC'.encode('ascii'))
print('中午'.encode('utf-8'))
print(b'ABC'.decode('ascii'))
print(b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8'))
# b'ABC'
# b'\xe4\xb8\xad\xe5\x8d\x88'
# ABC
# 中文
print(len('ABC')) #3
print(len('中午')) #2
print(len(b'ABC')) #3
print(len(b'\xe4\xb8\xad\xe6\x96\x87')) #6
print(len('中文'.encode('utf-8'))) #6
print('Hi, %s, you are %d.' % ('yaya', 33))
s1 = 72
s2 = 85
r = (s2-s1)/s1
print('小明成绩提升了%.1f' % r)
# Hi, yaya, you are 33.
# 小明成绩提升了0.2
#List------------list和tuple是Python内置的有序集合,一个可变,一个不可变(指引不变)。根据需要来选择使用它们。--------------------------------------
classmates = ['Michael', 'Bob', 'Tracy']
print(classmates)
print(classmates[1])
print(classmates[-1])
print(len(classmates))
classmates.append('Adam')
print(classmates)
classmates.insert(1,'Jack')
print(classmates)
classmates.pop()
print(classmates)
classmates.pop(1)
print(classmates)
classmates[0] = 'Red'
print(classmates)
t = ('Michael', 'Bob', 'Tracy')
t = ()
t = (1,)
height = 1.75
weight = 80.5
bmi = weight/(height*height)
if bmi > 32:
print('严重肥胖')
elif bmi > 28:
print('肥胖')
elif bmi > 25:
print('过重')
elif bmi > 18.5:
print('正常')
else:
print('过轻')
sum = 0
for x in range(100):
sum = sum + x
print(sum)
n = 0
while n <= 100:
if n > 5:
break
if n % 2 == 0:
continue
print(n)
n = n + 1
print('END')
break语句可以在循环过程中直接退出循环,而continue语句可以提前结束本轮循环,并直接开始下一轮循环。这两个语句通常都必须配合if语句使用。
Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。
请务必注意,dict内部存放的顺序和key放入的顺序是没有关系的。
和list比较,dict有以下几个特点:
查找和插入的速度极快,不会随着key的增加而变慢;
需要占用大量的内存,内存浪费多。 而list相反:
查找和插入的时间随着元素的增加而增加;
占用空间小,浪费内存很少。
所以,dict是用空间来换取时间的一种方法。
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
定义函数时,需要确定函数名和参数个数;
如果有必要,可以先对参数的数据类型做检查;
函数体内部可以用return随时返回函数结果;
函数执行完毕也没有return语句时,自动return None。
函数可以同时返回多个值,但其实就是一个tuple。
如果想定义一个什么事也不做的空函数,可以用pass语句:
def nop():
pass
请定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程的两个解
import math
def quadratic(a, b, c):
x1 = (-b + math.sqrt(b*b - 4*a*c))/(2*a)
x2 = (-b - math.sqrt(b*b - 4*a*c))/(2*a)
return x1,x2
# 测试:
print('quadratic(2, 3, 1) =', quadratic(2, 3, 1))
print('quadratic(1, 3, -4) =', quadratic(1, 3, -4))
if quadratic(2, 3, 1) != (-0.5, -1.0):
print('测试失败')
elif quadratic(1, 3, -4) != (1.0, -4.0):
print('测试失败')
else:
print('测试成功')
Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。
默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
要注意定义可变参数和关键字参数的语法:
*args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict。
以及调用函数时如何传入可变参数和关键字参数的语法:
可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过args传入:func((1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过kw传入:func({'a': 1, 'b': 2})。
使用*args和**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
def fact(n):
return fact_iter(n, 1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
fact_iter(5, 1)的调用如下:
===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120
def move(n, a, b, c):
if n == 1:
print('move', a, '-->', c)
else:
move(n-1, a, c, b)
move(1, a, b, c)
move(n-1, b, a, c)
# 期待输出:
# A --> C
# A --> B
# C --> B
# A --> C
# B --> A
# B --> C
# A --> C
move(3, 'A', 'B', 'C')
def trim(s):
while(len(s) and s[0] == ' '):
s = s[1:]
while(len(s) and s[-1] == ' '):
s = s[:-1]
return s
# 测试:
if trim('hello ') != 'hello':
print('测试失败!')
elif trim(' hello') != 'hello':
print('测试失败!')
elif trim(' hello ') != 'hello':
print('测试失败!')
elif trim(' hello world ') != 'hello world':
print('测试失败!')
elif trim('') != '':
print('测试失败!')
elif trim(' ') != '':
print('测试失败!')
else:
print('测试成功!')
def findMinAndMax(L):
if len(L) == 0:
return None, None
elif len(L) == 1:
return L[0],L[0]
else:
maxN = max(L)
minN = min(L)
return minN, maxN
# 测试
if findMinAndMax([]) != (None, None):
print('测试失败!')
elif findMinAndMax([7]) != (7, 7):
print('测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
print('测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
print('测试失败!')
else:
print('测试成功!')
任何可迭代对象都可以作用于for循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for循环。
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a = [x * x for x in range(1, 11) if x % 2 == 0]
print(a) #[4, 16, 36, 64, 100]
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [s.lower() for s in L1 if isinstance(s, str)]
# 测试:
print(L2)
if L2 == ['hello', 'world', 'apple']:
print('测试通过!')
else:
print('测试失败!')
运用列表生成式,可以快速生成list,可以通过一个list推导出另一个list,而代码却十分简洁。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。
著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
def fib(max):
n,a,b=0,0,1
while n<max:
yield(b)
a,b=b,a+b
n=n+1
return 'done'
print(fib(8)) #<generator object fib at 0x107a4c9a8>
def trianglesMid(L):
n = 1
result = [1]
while n < len(L):
m = L[n-1] + L[n]
result.append(m)
n = n + 1
result.append(1)
return result
print(trianglesMid([1,1]))
def triangles():
n,a,b=0,1,1
while n < 10000000000:
if n == 0:
b = [1]
yield b
elif n == 1:
b = [1,1]
yield b
else:
b = trianglesMid(a)
yield b
a = b
n = n + 1
return 'done'
# 期待输出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
results.append(t)
n = n + 1
if n == 10:
break
for t in results:
print(t)
if results == [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
print('测试通过!')
else:
print('测试失败!')
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
而函数式编程(请注意多了一个“式”字)——Functional Programming,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
def f(x):
return x*x
nums = [1,2,3,4,5,6,7,8,9,10]
r = map(f,nums)
print(list(r)) #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
from functools import reduce
def add(x,y):
return x+y
r = reduce(add,nums)
print(r) #55
#把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。
def normalize(name):
return name.lower().capitalize()
# 测试:
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2) #['Adam', 'Lisa', 'Bart']
#Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:
def prod(L):
def ji(x,y):
return x*y
return reduce(ji,L)
print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))
if prod([3, 5, 7, 9]) == 945:
print('测试成功!')
else:
print('测试失败!')
#利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def intgerFn(x,y):
return x*10+y
def char2num(n):
return DIGITS[n]
def str2float(s):
dotIndex = s.find('.')
newS = s.replace('.','')
intger = reduce(intgerFn, map(char2num, newS))
if dotIndex > -1:
floatNum = len(s) - 1 - dotIndex
intger = intger / pow(10,floatNum)
return intger
print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。
#回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
def is_palindrome(n):
return str(n) == str(n)[::-1]
# 测试:
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
if list(filter(is_palindrome, range(1, 200))) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]:
print('测试成功!')
else:
print('测试失败!')
sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
return t[0].lower()
L2 = sorted(L, key=by_name)
print(L2) #[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
def by_score(t):
return -t[1]
L2 = sorted(L, key=by_score)
print(L2) #[('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
f = lazy_sum(1, 3, 5, 7, 9)
f() #25
在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False
f1()和f2()的调用结果互不影响。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
f1, f2, f3 = count()
print(f1()) #1
print(f2()) #4
print(f3()) #9
利用闭包返回一个计数器函数,每次调用它返回递增整数:
def createCounter():
cnt = [0]
def counter():
cnt[0] = cnt[0] + 1
return cnt[0]
return counter
# 测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
print('测试通过!')
else:
print('测试失败!')
一个函数可以返回一个计算结果,也可以返回一个函数。
返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
通过对比可以看出,匿名函数lambda x: x * x实际上就是:
def f(x):
return x * x
改造前:
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))
print(L)
改造后:
LL = list(filter(lambda x:x%2==1,range(1,20)))
print(LL)
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
>>> def now():
... print('2015-3-25')
...
>>> f = now
>>> f()
2015-3-25
函数对象有一个__name__属性,可以拿到函数的名字:
>>> now.__name__
'now'
>>> f.__name__
'now'
现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
import time, functools
def metric(fn):
def wrapper(*args, **kw):
print('%s executed in %s ms' % (fn.__name__, 10.24))
return fn(*args, **kw)
return wrapper
# 测试
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y;
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z;
f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
自己创建模块时要注意命名,不能和Python自带的模块名称冲突。例如,系统自带了sys模块,自己的模块就不可命名为sys.py,否则将无法导入系统自带的sys模块。
模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用。
创建自己的模块时,要注意:
类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__,__name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;
类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;
之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。