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允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

  • 相关阅读:
    HTML: 表单标签、CSS语法、CSS选择器、CSS属性
    HTML:快速入门、表格标签
    JDBC连接池&JDBCTemplate
    JDBC
    MYSQL多表查询&事务
    使用CompletionService批处理任务(线程池阻塞线程)
    java运行字符串代码
    Linux常用命令大全(非常全!!!)
    SpringBoot防止重复请求,重复表单提交超级简单的注解实现
    在Spring-boot中,为@Value注解添加从数据库读取properties支持
  • 原文地址:https://www.cnblogs.com/tank-/p/8376341.html
Copyright © 2011-2022 走看看