从 Python到Tensorflow 学习之路(二)


最近毕业设计题目是研究对抗样本,要用tensorflow来搭建神经网络,因此python必不可少,这个不是一个传统的Python学习教程只是把学习Python过程中遇到的问题和经验记录下来(基于Python3.5),如果想要一步一步学习Python建议看下面的网站。
Python学习教程


Python中的迭代

只要是可迭代对象,无论是否有下标,都可以迭代,例如dict:

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

dict = {'a':1, 'b':2,'c':3}
# 默认情况下dict迭代的是key, dict存储不是按照list存储, 所以迭代的结果很可能不一样
for key in dict:
    print(key)

# 迭代value
for value in dict.values():
    print(value)

# 迭代value和key
for key,value in dict.items():
    print(key, value)

判断一个类型是否可以迭代

# 判断一个类型是否可以迭代
from collections import Iterable

print(isinstance('abc', Iterable))

print(isinstance([1,  2, 3], Iterable))

print (isinstance(123, Iterable))

对list实现类似Java的下标循环,让其变成索引元素对

l = ['a', 'b', 'd', 'e']

for index, value in enumerate(l):
    print(index, value)

列表生成式即List Comprehensions, 可以用来创建list的生成式

print (list(range(1, 11)))

print ([x*x for x in range(1, 11)])

# 生成全排列
print ([m+n for m in 'ABC' for n in 'DEF'])

# 列出当前目录下的所有文件和目录名
import os
print([d for d in os.listdir('.')])

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含几百万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。从下面可以看出生成式g有点神似指针。。。

# 生成式
g = (x*x for x in range(1, 11))
print(g)

# output: <generator object <genexpr> at 0x7f2518e03910>

print(next(g))
# output: 1

print(next(g))
# output: 4

for i in g:
    print(i)
# !注意输出为9, 16, ...

# 利用yield产生斐波那契数列
def fib(n):
    i, a, b = 0, 0, 1
    while i < n:
        yield b
        a, b = b, a+b
        i = i + 1
    return 'done'

f = fib(6)

for i in f:
    print(i)

可以直接作用于for循环的数据类型有以下几种:

  • 一类是集合数据类型,如list、tuple、dict、set、str等
  • 一类是generator,包括生成器和带yield的generator function
    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
from collections import Iterable

# 使用isinstance判断一个对象是否是Iterable对象
print(isinstance([], Iterable))
# output: True

print(isinstance((), Iterable))
# output: True

print(isinstance('abc', Iterable))
# output: True

print(isinstance((x*x for x in range(10)), Iterable))
# output: True

print(isinstance(100,Iterable))
# output: False

Iterator对象:生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到抛出stopIteration错误表示无法继续获取下一个值。可以被next()函数调用并且不断返回下一个值的对象叫做迭代器,可以使用isinstance()判断一个对象是否是Iterator对象。

将list、dict、str等Iterable对象变成Iterator,使用iter()函数

print(isinstance(iter(()), Iterator))
# output:True

print(isinstance(iter([]), Iterator))
# output:True

Python中的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但是我们不能提前直到序列的长度,只能不断通过next()函数按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据,例如全体自然数,然而list不行。

for循环的本质

it = iter([1, 2, 3, 4, 5])

while True:
    try:
        print(next(it))
    except StopIteration:
        break

Python中的函数式编程

变量可以指向函数:函数本身也可以赋值给变量,通过变量可以调用函数

print(abs(-1.5))
# output: 1.5

print(abs)
# output: <built-in function abs>

f = abs
print(f(-1.5))
# output: 1.5

传入函数:一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数

def add(x, y, f):
    return f(x)+f(y)


print(add(1, -1, abs))
# output: 2

Map、reduce函数

map函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并且把结果作为新的Iterator返回。

def f(x):
    return x*x


r = map(f, [1, 2, 3, 4, 5, 6])
print(list(r))

res_str = map(str, [1, 2, 3, 4, 5, 6])
print(list(res_str))

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

reduce(f,[x1,x2,x3])=f(f(x1,x2),x3)

from functools import reduce


def fn(x, y):
    return x * 10 + y


print(reduce(fn, [1, 4, 3, 3]))
# output:1433


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]


print(reduce(fn, map(char2num, '1433')))

利用lambda函数整理成一个str2int函数

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}


def char2num(s):
    return DIGITS[s]


def str2num(s):
    return reduce(lambda x, y: 10*x+y, map(char2num, s))


print(str2num('1433'))

filter

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

# 筛选奇数


def is_odd(n):
    return n % 2 == 1


print(list(filter(is_odd, [1, 2, 4, 3])))
# filter产生的是惰性序列即Iterator, 需要用list函数获得所有结果, output:[1, 3]

sorted

print(sorted([1, 2, 3, -1, 2], key=abs))
# output:[1, -1, 2, 2, 3]

print(sorted(['bob', 'alice', 'John', 'hinton'], key=str.lower, reverse=True))
# output:['John','hinton','bob','alice']

返回函数

def lazy_sum(*args):
    def sum():
        res = 0
        for n in args:
            res += n
        return res
    return sum


f = lazy_sum(1, 2, 3, 4, 5)
print(f)
# output: <function lazy_sum.<locals>.sum at 0x7f995d0fb840>
print(f())
# output: 15

f1 = lazy_sum(0, 1)
f2 = lazy_sum(1, 2)
print(f1 == f2)
# output: False

一个函数可以返回一个计算结果,也可以返回一个函数。当返回一个函数时,这个函数并未执行,返回函数中不要引用任何可能会变化的变量。

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


f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())
# output:9 9 9
# 返回函数引用了变量i, 但它并非立刻被执行.等到三个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9

修改后

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


f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())
# output:1, 4, 9

匿名函数

关键字lambda表示匿名函数,冒号前面的表示函数参数。匿名函数可以作为返回值返回

print(list(map(lambda x: x*x, [1, 2, 3])))
# output: [1, 4, 9]

f = lambda x: x*x
print(f)
# output:<function <lambda> at 0x7f6966041f28>

def cacl(x, y):
    return lambda:x*x+y*y
print(cacl(1,3))
# output:<function cacl.<locals>.<lambda> at 0x7efc34e652f0>
print(cacl(1,3)())
# output: 10

装饰器

函数也是一个对象,而且函数对象可以被赋值给变量,所以通过变量也能调用该函数。通过函数对象的一个属性__name__,我们可以得到函数的名字。

def now():
    print('2017-12-21')


f = now
print(f.__name__)
# output:now

如果我们要增强now()函数的功能,比如在函数调用前后自动打印日志,但是又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器(Decorator)”

def log(func):
    def warpper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return warpper


@log
def now():
    print('2017-12-21')


# output:call now()
# 2017-12-21

@log放到now()函数的定义处,相当于执行了语句now = log(now)。由于log()是一个decorator,返回一个函数,所以原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。wrapper()函数的参数定义是(*args, **kw),因此,warpper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

如果decorator本身需要传入参数,那么需要编写一个返回decorator的高阶函数,如定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator


@log('execute')
def now():
    print('2017-12-21')


now()

# output:execute now():
# 2017-12-21

print(now.__name__)
# wrapper

和两层嵌套的decorator相比,三层嵌套的效果是这样的:now=log('execute')(now),首先执行log('execute'),返回的是decorator函数,再调用返回的参数,参数是now函数,返回值最终是wrapper函数。但是函数也是对象,它有name等属性,但上面代码中的经过decorator装饰过的函数,它们的名字已经从原来的now变成了wrapper

我们需要把原始函数的各种属性复制到wrapper()函数中,否则有些依赖函数签名的代码执行就会出错。修改后如下

import functools


def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator


@log('execute')
def now():
    print('2017-12-21')


now()

# output:execute now():
# 2017-12-21

print(now.__name__)
# now

偏函数

import functools

print(int('12345'))

print(int('12345', base=8))

# 如果要转换大量的二进制字符串,每次都传入int(x, base=2)太麻烦,容易想到的方法如下


def int2(s, base=2):
    return int(s, base)


print(int2('10000'))
# output:16


# 使用偏函数

int2 = functools.partial(int, base=2)
print(int2('11'))
# output:3
''' 
   int2('11')相当于kw={'base': 2} int('11', **kw)
   当传入max2 = functools.partial(max, 10)实际上会把10作为*args的一部分自动加到左边,也就是:
   max2(5, 6, 7)相当于args=(10, 5, 6, 7) max(*args)
'''

functools.partial的作用就是,把函数的某些参数给固定住,返回一个新的函数,调用这个新函数会更加简单。创建偏函数时,实际上可以接收函数对象、*args**kw三个参数

我们下期见!~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章