python学习笔记(一)

python学习笔记(一)

参照:廖雪峰的python教程

一、python基础

1、数据类型和变量

1)r' '表示' '内部的字符串默认不转义
‘’’…’’'的格式表示多行内容

print(r'''hello,\n
world''')
hello,\n 
world 

2)//:地板除,两个整数的除法仍然是整数
Python的整数没有大小限制

2、字符串和编码

1)ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:
在这里插入图片描述
浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:<meta charset="UTF-8" />
在这里插入图片描述
2)ord():获取单个字符的编码的整数表示
chr():把编码转换为对应的字符

字符串类型:str 内存:Unicode表示
在网络上传输,或者保存到磁盘上:str=>bytes
bytes:b' '

str=>bytes encode
bytes=>str decode
\x :显示ASCII字符的字节

errors='ignore':忽略错误的字节

>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'

1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。
当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

# -*- coding: utf-8 -*-

3)%:输出格式化的字符串

>>> 'Hello, %s' % 'world'
'Hello, world'

%%:转义一个%

>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'

format()

3、使用list和tuple

list是一个可变的有序表 []
tuple 元组 一旦初始化就不能修改 ()
只有1个元素的tuple定义时必须加一个逗号
“可变的”tuple:

>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])

4、条件判断

5、循环

6、使用dict和set

1)dict 字典 使用键-值(key-value)存储,具有极快的查找速度

>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95

放入数据到dict:通过key放入:

>>> d['Adam'] = 67
>>> d['Adam']
67

判断key是否存在:
in

>>> 'Thomas' in d
False

get()

>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1

2)比较list、dict:
dict: 查找和插入的速度极快,不会随着key的增加而变慢;
需要占用大量的内存,内存浪费多。

list: 查找和插入的时间随着元素的增加而增加;
占用空间小,浪费内存很少。
dict的key必须是不可变对象

3)set
set和dict类似,key的集合,但不存储value key不能重复

要创建一个set,需要提供一个list作为输入集合:

>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}

重复元素在set中自动被过滤
set不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。

二、函数

1、调用函数

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:

>>> a = abs # 变量a指向abs函数
>>> a(-1) # 所以也可以通过a调用abs函数
1

2、定义函数

如果没有return语句,函数执行完毕后也会返回结果,只是结果为None
空函数:

def nop():
	pass

pass可以用来作为占位符
据类型检查可以用内置函数isinstance()实现

def my_abs(x):
    if not isinstance(x, (int, float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

3、函数的参数

1)定义默认参数要牢记一点:默认参数必须指向不变对象!
不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

2)定义可变参数:在参数前面加了一个*号,可变参数在函数调用时自动组装为一个tuple

3)关键字参数:关键字参数在函数内部自动组装为一个dict

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
>>> person('Michael', 30)
name: Michael age: 30 other: {}

关键字参数有什么用?扩展函数的功能。

4、递归的参数

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归 在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

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)

三、高级特性

1、切片

取前3个元素

>>> L[0:3] 	# L[:3]
['Michael', 'Sarah', 'Tracy']

L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3
倒数第一个元素的索引是-1

2、迭代

如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:

>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False

3、列表生成式

List Comprehensions

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

4、生成器

一边循环一边计算的机制,称为生成器:generator
1)把一个列表生成式的[]改成()

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代
2)函数定义中包含yield关键字
generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

5、迭代器

使用isinstance()判断一个对象是否是Iterator对象

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误,数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

四、函数式编程

1、高阶函数

1)变量可以指向函数

>>> f = abs
>>> f(-10)
10

函数名也是变量
传入函数:既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数
2)Python内建了map()和reduce()函数
map()函数接收两个参数,一个是函数,一个是Iterable

>>> 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]

Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。

reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

    >>> from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

3)filter()函数用于过滤序列
filter()接收一个函数和一个序列,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。惰性序列

def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# 结果: ['A', 'B', 'C']

用fliter求素数

# 构造一个从3开始的奇数序列,这是一个生成器,并且是一个无限序列
def _odd_iter():
    n = 1
    while True:
        n += 2
        yield n
# 定义一个筛选函数
def _not_divisible(n):
    return lambda x: x % n > 0     
# 定义一个生成器,不断返回下一个素数
def primes():
    yield 2
    it = _odd_iter() # 初始序列
    while True:
        n = next(it) # 返回序列的第一个数
        yield n
        it = filter(_not_divisible(n), it) # 构造新序列      
# 打印1000以内的素数:
for n in primes():
    if n < 1000:
        print(n)
    else:
        break         

打印回数

def is_palindrome(number):
    ch = str(number)
    return ch[:] == ch[::-1]

output = filter(is_palindrome, range(1, 1000))
print(list(output))

4)sorted

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

用sorted()排序的关键在于实现一个映射函数

2、返回函数

1)函数作为返回值

>>> def lazy_sum(*args):
...     def sum():
...             ax = 0
...             for n in args:
...                     ax = ax + n
...             return ax
...     return sum
...
# 调用lazy_sum()时,返回的并不是求和结果,而是求和函数
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x007F07C8>
# 调用函数f时,才真正计算求和的结果
>>> f() 
25
>>>
# 当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1 == f2
# f1()和f2()的调用结果互不影响
False

2)闭包
返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用

返回的函数并没有立刻执行,而是直到调用了f()才执行

>>> def count():
...     fs = []
...     for i in range(1, 4):
...             def f():
...                     return i * i
...             fs.append(f)
...     return fs
...
>>> f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9

返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

>>> def count():
...     def f(j):
...             def g():
...                     return j * j
...             return g
...     fs = []
...     for i in range(1, 4):
...             fs.append(f(i))
...     return fs
...
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

缺点是代码较长,可利用lambda函数缩短代码。
返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

3、匿名函数

传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。以map()函数为例

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

关键字lambda表示匿名函数,冒号前面的x表示函数参数

匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

用匿名函数不必担心函数名冲突
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数,也可以把匿名函数作为返回值返回

4、装饰器

函数对象有一个__name__属性,可以拿到函数的名字
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。decorator就是一个返回函数的高阶函数

>>> def log(func):
...     def wrapper(*args, **kw):
...             print('call %s():' % func.__name__)
...             return func(*args, **kw)
...     return wrapper
...
>>> @log
... def now():
...     print('2019-2-14')
...
# 调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:
>>> now()
call now():
2019-2-14
# 把@log放到now()函数的定义处,相当于执行了语句
>>> now = log(now)
>>>> now()
call wrapper():
call now():
2019-2-14

log()是一个decorator,返回一个函数,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值