python学习——知识点汇总——基础部分

2020-05-26 loading

# 简介

Python是著名的“龟叔”Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言。

C语言是可以用来编写操作系统的贴近硬件的语言,所以,C语言适合开发那些追求运行速度、充分发挥硬件性能的程序。而Python是用来编写应用程序的高级编程语言。 总的来说,Python的哲学就是简单优雅,尽量写容易看明白的代码,尽量写少的代码。

python缺点:

  • 运行速度慢,和C程序相比非常慢,因为Python是解释型语言,你的代码在执行时会一行一行地翻译成CPU能理解的机器码,这个翻译过程非常耗时,所以很慢。而C程序是运行前直接编译成CPU能执行的机器码,所以非常快。
  • 代码不能加密。

# 基础

  • Python程序是大小写敏感的,如果写错了大小写,程序会报错
  • Python使用缩进来组织代码块,请务必遵守约定俗成的习惯,坚持使用4个空格的缩进
  • 在文本编辑器中,需要设置把Tab自动转换为4个空格,确保不混用Tab和空格

# 数据类型和变量

  • 整数
  • 浮点数
  • 字符串
  • 布尔值
  • 控制None
  • 变量
  • 常量

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和tuple

#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语句使用。

# 使用dict和set

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)

著名的斐波拉契数列(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/reduce

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('测试失败!')

# filter

和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()也是一个高阶函数。用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代码的集合,可以使用其他模块,也可以被其他模块使用。

创建自己的模块时,要注意:

  • 模块名要遵循Python变量命名规范,不要使用中文、特殊字符;
  • 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行import abc,若成功则说明系统存在此模块。

# 作用域

  • 类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__,__name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;

  • 类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;

之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。

# 备注

https://www.liaoxuefeng.com/wiki/1016959663602400(opens new window)

支付宝打赏
支付宝打赏
微信打赏
微信打赏