zoukankan      html  css  js  c++  java
  • python 基础

    1、dict key 不存在的情况

    要避免key不存在的错误,有两种办法,一是通过in判断key是否存在:

    >>> 'Thomas' in d False 

    二是通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value:

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

    注意:返回None的时候Python的交互环境不显示结果。

     

    删除一个key,用pop(key)方法,对应的value也会从dict中删除:

    >>> d.pop('Bob') 75 >>> d {'Michael': 95, 'Tracy': 85}

    set

    set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。

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

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

    注意,传入的参数[1, 2, 3]是一个list,而显示的{1, 2, 3}只是告诉你这个set内部有1,2,3这3个元素,显示的顺序也不表示set是有序的。。

    重复元素在set中自动被过滤:

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

    通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果:

    >>> s.add(4) >>> s {1, 2, 3, 4} >>> s.add(4) >>> s {1, 2, 3, 4} 

    通过remove(key)方法可以删除元素:

    >>> s.remove(4) >>> s {1, 2, 3} 

    set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:

    >>> s1 = set([1, 2, 3]) >>> s2 = set([2, 3, 4]) >>> s1 & s2 {2, 3} >>> s1 | s2 {1, 2, 3, 4} 

    set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。试试把list放入set,看看是否会报错。

    再议不可变对象

    上面我们讲了,str是不变对象,而list是可变对象。

    2、函数
    求绝对值的函数abs
    >>> abs(-20) 20

    定义函数时,需要确定函数名和参数个数;

    如果有必要,可以先对参数的数据类型做检查;

     if not isinstance(x, (int, float)):
    raise TypeError('bad operand type')
     

    max函数max()可以接收任意多个参数,并返回最大的那个:

    >>> max(1, 2) 2
    Python内置的hex()函数把一个整数转换成十六进制表示的字符串
    return None可以简写为return

    返回多个值

    原来返回值是一个tuple()!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。
    计算平方根可以调用math.sqrt()函数

    默认参数可以简化函数的调用。设置默认参数时,有几点要注意:

    一是必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);

    二是如何设置默认参数。

    当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。

    使用默认参数有什么好处?最大的好处是能降低调用函数的难度。

    当不按顺序提供部分默认参数时,需要把参数名写上

    默认参数很有用,但使用不当,也会掉坑里。默认参数有个最大的坑,演示如下:

    先定义一个函数,传入一个list,添加一个END再返回:

    def add_end(L=[]):     L.append('END')     return L 

    当你正常调用时,结果似乎不错:

    >>> add_end([1, 2, 3]) [1, 2, 3, 'END'] >>> add_end(['x', 'y', 'z']) ['x', 'y', 'z', 'END'] 

    当你使用默认参数调用时,一开始结果也是对的:

    >>> add_end() ['END'] 

    但是,再次调用add_end()时,结果就不对了:

    >>> add_end() ['END', 'END'] >>> add_end() ['END', 'END', 'END'] 

    很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了'END'后的list。

    原因解释如下:

    Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

     定义默认参数要牢记一点:默认参数必须指向不变对象!

    要修改上面的例子,我们可以用None这个不变对象来实现:

    def add_end(L=None):     if L is None:         L = []     L.append('END')     return L 

    现在,无论调用多少次,都不会有问题:

    >>> add_end() ['END'] >>> add_end() ['END'] 

    为什么要设计strNone这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

     

    把函数的参数改为可变参数:

    def calc(*numbers):     sum = 0     for n in numbers:         sum = sum + n * n     return sum 

    定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:

    >>> calc(1, 2) 5 >>> calc() 0 

    如果已经有一个list或者tuple,要调用一个可变参数怎么办?可以这样做:

    >>> nums = [1, 2, 3] >>> calc(nums[0], nums[1], nums[2]) 14 

    这种写法当然是可行的,问题是太繁琐,所以Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:

    >>> nums = [1, 2, 3] >>> calc(*nums) 14 

    *nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。

    命名关键字参数

    对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。

    仍以person()函数为例,我们希望检查是否有cityjob参数:

    def person(name, age, **kw):     if 'city' in kw:         # 有city参数         pass     if 'job' in kw:         # 有job参数         pass     print('name:', name, 'age:', age, 'other:', kw) 

    但是调用者仍可以传入不受限制的关键字参数:

    >>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456) 

    如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收cityjob作为关键字参数。这种方式定义的函数如下:

    def person(name, age, *, city, job):     print(name, age, city, job) 

    和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。

    调用方式如下:

    >>> person('Jack', 24, city='Beijing', job='Engineer') Jack 24 Beijing Engineer

    使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python解释器将无法识别位置参数和命名关键字参数:

    def person(name, age, city, job):     # 缺少 *,city和job被视为位置参数     pass
    对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

     3、迭代器:::

     可以使用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对象,但listdictstr虽然是Iterable,却不是Iterator

    listdictstrIterable变成Iterator可以使用iter()函数:

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

    你可能会问,为什么listdictstr等数据类型不是Iterator

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

    Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

    小结

    凡是可作用于for循环的对象都是Iterable类型;

    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

    集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

    Python的for循环本质上就是通过不断调用next()函数实现的

    4、高阶函数

     map()

    把这个list所有数字转为字符串:

    >>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) 

    ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 

    reduce()

    就可以用reduce实现:

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

    25 

     整理成一个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 str2int(s):
    def fn(x, y):
    return x * 10 + y
    def char2num(s):
    return DIGITS[s]
    return reduce(fn, map(char2num, s))
    

    还可以用lambda函数进一步简化成:

    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 str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))
    

    也就是说,假设Python没有提供int()函数,你完全可以自己写一个把字符串转化为整数的函数,而且只需要几行代码!

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

    例如,在一个list中,删掉偶数,只保留奇数,可以这么写:

    def is_odd(n):
    return n % 2 == 1
    list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
    # 结果: [1, 5, 9, 15]
    

    把一个序列中的空字符串删掉,可以这么写:

    def not_empty(s):
    return s and s.strip()
    list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
    # 结果: ['A', 'B', 'C']
    

    可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。

    注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

    得到所有的素数。

    用Python来实现这个算法,可以先构造一个从3开始的奇数序列:

    def _odd_iter():     n = 1     while True:         n = 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) # 构造新序列 

    这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列。

    由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件:

    # 打印1000以内的素数: for n in primes():     if n < 1000:         print(n)     else:         break 

    注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁。

     排序:

    Python内置的sorted()函数就可以对list进行排序:

    >>> sorted([36, 5, -12, 9, -21]) [-21, -12, 5, 9, 36] 

    此外,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

    >>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36]

     对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。

    忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。

    这样,我们给sorted传入key函数,即可实现忽略大小写的排序:

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

    要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True

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

    从上述例子可以看出,高阶函数的抽象能力是非常强大的,而且,核心代码可以保持得非常简洁。

     返回函数,闭包:

    注意到返回的函数在其定义内部引用了局部变量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() 

    在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。

    你可能认为调用f1()f2()f3()结果应该是149,但实际结果是:

    >>> f1() 9 >>> f2() 9 >>> f3() 9 

    全部都是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)) # f(i)立刻被执行,因此i的当前值被传入f()     return fs 

    再看看结果:

    >>> f1, f2, f3 = count() >>> f1() 1 >>> f2() 4 >>> f3() 9 

    缺点是代码较长,可利用lambda函数缩短代码。

     匿名函数:

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

    用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:

    >>> f = lambda x: x * x >>> f <function <lambda> at 0x101c6ef28> >>> f(5) 25 

    同样,也可以把匿名函数作为返回值返回,比如:

    def build(x, y):     return lambda: x * x + y * y

     装饰器:

    在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
     

    本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

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

    观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

    @log def now():     print('2015-3-25') 

    调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

    >>> now() call now(): 2015-3-25

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

    这个3层嵌套的decorator用法如下:

    @log('execute') def now():     print('2015-3-25') 

    执行结果如下:

    >>> now() execute now(): 2015-3-25 

    和两层嵌套的decorator相比,3层嵌套的效果是这样的:

    >>> now = log('execute')(now)

    一个完整的decorator的写法如下:

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

    或者针对带参数的decorator:

    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 

    import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

     偏函数:

    functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2

    >>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85 

    所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

    注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:

    >>> int2('1000000', base=10) 1000000 

    最后,创建偏函数时,实际上可以接收函数对象、*args**kw这3个参数,当传入:

    int2 = functools.partial(int, base=2) 

    实际上固定了int()函数的关键字参数base,也就是:

    int2('10010') 

    相当于:

    kw = { 'base': 2 } int('10010', **kw) 

    当传入:

    max2 = functools.partial(max, 10) 

    实际上会把10作为*args的一部分自动加到左边,也就是:

    max2(5, 6, 7) 

    相当于:

    args = (10, 5, 6, 7) max(*args) 

    结果为10

     使用模块:

    编写一个hello的模块:

    #!/usr/bin/env python3 # -*- coding: utf-8 -*-  
    ' a test module '  
    __author__ = 'Michael Liao'  
    import sys  def test():     
    args = sys.argv
         if len(args)==1:
             print('Hello, world!')
         elif len(args)==2
            print('Hello, %s!' % args[1])
         else
            print('Too many arguments!')
      if __name__=='__main__':     test()

    导入sys模块后,我们就有了变量sys指向该模块,利用sys这个变量,就可以访问sys模块的所有功能。

    sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:

    运行python3 hello.py获得的sys.argv就是['hello.py']

    运行python3 hello.py Michael获得的sys.argv就是['hello.py', 'Michael]

     安装第三方模块:

    我们推荐直接使用Anaconda

     这是一个基于Python的数据处理和科学计算平台,

    它已经内置了许多非常有用的第三方库,我们装上Anaconda,就相当于把数十个第三方模块自动安装好了,非常简单易用。

    默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:

    如果我们要添加自己的搜索目录,有两种方法:

    一是直接修改sys.path,添加要搜索的目录:

    >>> import sys >>> sys.path.append('/Users/michael/my_py_scripts') 

    这种方法是在运行时修改,运行结束后失效。

    第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。

     面向对象编程:

     类和实例

    由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把namescore等属性绑上去:

    class Student(object):      def __init__(self, name, score):         self.name = name         self.score = score

    可以自由地给一个实例变量绑定属性,比如,给实例bart绑定一个name属性:

    >>> bart.name = 'Bart Simpson' >>> bart.name 'Bart Simpson'

    小结

    类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

    方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;

    通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

    和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

  • 相关阅读:
    ACM ICPC 2008–2009 NEERC MSC A, B, C, G, L
    POJ 1088 滑雪 DP
    UVA 11584 最短回文串划分 DP
    POJ 2531 Network Saboteur DFS+剪枝
    UVa 10739 String to Palindrome 字符串dp
    UVa 11151 Longest Palindrome 字符串dp
    UVa 10154 Weights and Measures dp 降维
    UVa 10271 Chopsticks dp
    UVa 10617 Again Palindrome 字符串dp
    UVa 10651 Pebble Solitaire 状态压缩 dp
  • 原文地址:https://www.cnblogs.com/tank-/p/8376341.html
Copyright © 2011-2022 走看看