• 【廖雪峰】Python



    Python是一种相当高级的语言。
    TIOBE排行榜
    高级编程语言通常都会提供一个比较完善的基础代码库,让你能直接调用,
    许多大型网站就是用Python开发的,例如YouTube、Instagram,还有国内的豆瓣。
    Python的哲学就是简单优雅

    那Python适合开发哪些类型的应用呢? 
    • 网络应用,包括网站、后台服务等等; 
    • 脚本任务等等; 
    • 把其他语言开发的程序再包装起来,方便使用。
    缺点:
    • 运行速度慢:解释型语言,执行的时候翻译称为机器代码。
    • 代码不能加密:靠网站和移动应用卖服务的模式越来越多了

    安装

    Python3.5
    在Windows上运行Python时,请先启动命令行,然后运行python。 
    在Mac和Linux上运行Python时,请打开终端,然后运行python3。

    Python解释器

    Cpython:官方

    第一个Python程序

    退出:exit()

    命令行模式和Python交互模式

    python calc.py

    使用文本编辑器

    1. #!/usr/bin/env python3
    2. print('hello, world')
    $ chmod a+x hello.py
    直接输入python进入交互模式,相当于启动了Python解释器,但是等待你一行一行地输入源代码,每输入一行就执行一行。 
    直接运行.py文件相当于启动了Python解释器,然后一次性把.py文件的源代码给执行了,你是没有机会以交互的方式输入源代码的。

    Python代码运行助手

    网页上输入代码,通过本机的Python库进行解释。

    输入和输出

    print()会依次打印每个字符串,遇到逗号“,”会输出一个空格,因此,输出的字符串是这样拼起来的:
    print()也可以打印整数,或者计算结果

    输入

    name = input()
    提示字符串:
    name = input('please enter your name: ')

    python基础

    其他每一行都是一个语句,当语句以冒号:结尾时,缩进的语句视为代码块。
    缩进的坏处就是“复制-粘贴”功能失效了,
    最后,请务必注意,Python程序是大小写敏感的,如果写错了大小写,程序会报错。
    在文本编辑器中,需要设置把Tab自动转换为4个空格,确保不混用Tab和空格。

    变量和类型

    字符串
     
    字符串是以单引号'或双引号"括起来的任意文本
    >>> print(r'\\\t\\')
    用r''表示''内部的字符串默认不转义

    字符串有很多行:
    用\n写在一行里不好阅读,为了简化,Python允许用'''...'''的格式表示多行内容
    >>> print('''line1
    ... line2
    ... line3''')
    line1
    line2
    line3

    布尔值

    True、False
    if age >= 18:
        print('adult')
    else:
        print('teenager')

    空值

    空值是Python里一个特殊的值,用None表示。None不能理解为0,因为0是有意义的,而None是一个特殊的空值。

    变量

    理解变量在计算机内存中的表示也非常重要。当我们写:
     
    1. a = 'ABC'
    时,Python解释器干了两件事情:
     
    • 在内存中创建了一个'ABC'的字符串; 
    • 在内存中创建了一个名为a的变量,并把它指向'ABC'。

    a = 'ABC'
    b = a
    a = 'XYZ'print(b)

    执行a = 'ABC',解释器创建了字符串'ABC'和变量a,并把a指向'ABC'

    py-var-code-1

    执行b = a,解释器创建了变量b,并把b指向a指向的字符串'ABC'

    py-var-code-2

    执行a = 'XYZ',解释器创建了字符串'XYZ',并把a的指向改为'XYZ',但b并没有更改:

    py-var-code-3

    所以,最后打印变量b的结果自然是'ABC'了。

    一种除法是//,称为地板除,两个整数的除法仍然是整数:
    >>> 10 // 33
    Python还提供一个余数运算,可以得到两个整数相除的余数:
    >>> 10 % 31
    Python的整数没有大小限制,而某些语言的整数根据其存储长度是有大小限制的,例如Java对32位整数的范围限制在-2147483648~-2147483647。
     
    Python的浮点数也没有大小限制,但是超出一定范围就直接表示为inf(无限大)

    字符串和编码

    最早只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码
    但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。
    Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。

    如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。
    把Unicode编码转化为“可变长编码”的UTF-8编码
    常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节
    在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

    浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:
    web-utf-8
    所以你看到很多网页的源码上会有类似<meta charset="UTF-8" />的信息,表示该网页正是用的UTF-8编码。
    print('包含中文的str')
    >>> ord('A')
    65
    >>> chr(66)
    'B'
    ,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:

    如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。而在内存中用Unicode进行表示,一个字符对应多个字节
    x = b'ABC'
    注意区分'ABC'和b'ABC',前者是str,后者虽然内容显示得和前者一样,但bytes的每个字符都只占用一个字节。

    以Unicode表示的str通过encode()方法可以编码为指定的bytes
    >>> 'ABC'.encode('ascii')
    b'ABC'
    >>> '中文'.encode('utf-8')
    b'\xe4\xb8\xad\xe6\x96\x87'
    在bytes中,无法显示为ASCII字符的字节,用\x##显示。
    '中文'.encode('ascii')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
    因为中文编码的范围超过了ASCII编码的范围,Python会报错。
    如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法:

     b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
    '中文'
    计算字符数:len('中文')
    计算字节数:
    >>> len(b'ABC')
    3
    >>> len('中文'.encode('utf-8'))
    6
    为了避免乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。
    按照UTF-8进行读取:
    #!/usr/bin/env python3# -*- coding: utf-8 -*-

    格式化

    'Hello, %s' % 'world'
     'Hi, %s, you have $%d.' % ('Michael', 1000000)
    %d整数
    %f浮点数
    %s字符串
    %x十六进制整数
    >>> '%2d-%02d' % (3, 1)
    ' 3-01'>>> '%.2f' % 3.1415926'3.14'
    补零

    'Age: %s. Gender: %s' % (25, True)
    会把所有类型转换为字符串。
    用%%来表示一个%:

    list和tuple

    list


    。list是一种有序的集合,可以随时添加和删除其中的元素。

    classmates = ['Michael', 'Bob', 'Tracy']
     len(classmates)
    classmates[0]
    记得最后一个元素的索引是len(classmates) - 1。
    倒数第二个: classmates[-2]
    list是一个可变的有序表,所以,可以往list中追加元素到末尾:
    classmates.append('Adam')
    要删除list末尾的元素,用pop()方法:
    classmates.pop()
    要删除指定位置的元素,用pop(i)方法,其中i是索引位置:
    要把某个元素替换成别的元素,可以直接赋值给对应的索引位置:classmates[1] = 'Sarah'
    list里面的元素的数据类型也可以不同,比如:>>> L = ['Apple', 123, True]
    也可以是另外的list:s = ['python', 'java', ['asp', 'php'], 'scheme']
    要拿到'php'可以s[2][1],

    tuple


    元组
    一旦初始化以后就不能修改。

    1. classmates = ('Michael', 'Bob', 'Tracy')
    所以也没有append() , insert()这样的方法。
    代码可以更安全,尽可能使用。

    t = (1)既可能是表示元组,也可能是表示括号。
     t = (1,)
    消除歧义。
    >>> t = ('a', 'b', ['A', 'B'])
    >>> t[2][0] = 'X'>>> t[2][1] = 'Y'>>> t
    ('a', 'b', ['X', 'Y'])
    tuple包含3个元素。


    变的其实是list的元素。
    所谓的不变指的是指向永远不变。
    小结
     
    list和tuple是Python内置的有序集合,一个可变,一个不可变。


    条件判断

    age = 3if age >= 18:
        print('adult')
    elif age >= 6:
        print('teenager')
    else:
        print('kid')
    注意else后面的冒号。
    elif是else if 的缩写。
    如果在某个判断上是True,把该判断对应的语句执行后,就忽略掉剩下的elif和else
    birth = input('birth: ')
    if birth < 2000:
        print('00前')
    else:
        print('00后')
    输入的是字符串。但是2000是数字,不能比较。

    s = input('birth: ')
    birth = int(s)
    if birth < 2000:
        print('00前')
    else:
        print('00后')

    通过int(s)把字符串转换为数字。

    循环

    names = ['Michael', 'Bob', 'Tracy']
    for name in names:
        print(name)
    提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list。比如range(5)生成的序列是从0开始小于5的整数:
    list(range(5))
    range(101)就可以生成0-100的整数序列
    sum = 0
    n = 99while n > 0:
        sum = sum + n
        n = n - 2print(sum)
    在循环内部变量n不断自减,直到变为-1时,不再满足while条件,循环退出。

    使用dict和set

    dict

    其他语言map,键值对。
    d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
    >>> d['Michael']
    95
    在字典的索引表里(比如部首表)查这个字对应的页码,然后直接翻到该页,找到这个字。
    要避免key不存在的错误,有两种办法,一是通过in判断key是否存在:

    >>> 'Thomas' in d
    False
    二是通过dict提供的get方法,如果key不存在,可以返回None,或者自己指定的value:
    >>> d.get('Thomas')
    >>> d.get('Thomas', -1)
    -1
    要删除一个key,用pop(key)方法,对应的value也会从dict中删除:
    dict是用空间来换取时间的一种方法。
    dict的key必须是不可变对象
    通过key计算位置的算法称为哈希算法(Hash)。
    而list是可变的,就不能作为key:

    set


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

    s = set([1, 2, 3])
    重复元素自动过滤
     s = set([1, 1, 2, 2, 3, 3])
    通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果:
    通过remove(key)方法可以删除元素:
    set可以看成数学意义上的无序和无重复元素的集合

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

    再议不可变对象

    >>> a = ['c', 'b', 'a']
    >>> a.sort()
    >>> a
    ['a', 'b', 'c']
    内部对象是会变化的。
    >>> a = 'abc'>>> a.replace('a', 'A')
    'Abc'>>> a
    'abc'
    虽然replace可以变出Abc,但是实际上a仍然是abc
    a是变量,而'abc'才是字符串对象!有些时候,我们经常说,对象a的内容是'abc',但其实是指,a本身是一个变量,它指向的对象的内容才是'abc':

    replace方法创建了一个新字符串'Abc'并返回,如果我们用变量b指向该新字符串,就容易理解了,变量a仍指向原有的字符串'abc',但变量b却指向新字符串'Abc'了:

    对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。
    最常用的key是字符串。

    函数

    定义函数

    def my_abs(x):if x >= 0:
            return x
        else:
            return -x
    from abstest import my_abs来导入my_abs()函数,注意abstest是文件名(不含.py扩展名):

    空函数


    使用pass语句。
    def nop():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

    返回多个值

    import math语句表示导入math包,并允许后续代码引用math包里的sin、cos等函数。
    import math
    
    def move(x, y, step, angle=0):
        nx = x + step * math.cos(angle)
        ny = y - step * math.sin(angle)
        return nx, ny
    返回仍然是单一值,是一个tuple。

    函数的参数


    位置参数

    修改后的power(x, n)函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x和n。

    默认参数

    def power(x, n=2):
        s = 1while n > 0:
            n = n - 1
            s = s * x
        return s
    一是必选参数在前,默认参数在后,否则Python的解释器会报错
    二是如何设置默认参数。
    当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。

    def add_end(L=[]):
        L.append('END')
        return L
    此时默认参数是可变的list
    第一次调用
    >>> add_end()
    ['END']
    后面调用就不对了。
    >>> add_end()
    ['END', 'END']
    >>> add_end()
    ['END', 'END', 'END']

    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']
    由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。

    可变参数

    def calc(*numbers):
        sum = 0for n in numbers:
            sum = sum + n * n
        return sum
    Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:
    >>> nums = [1, 2, 3]
    >>> calc(*nums)
    14

    关键字参数

    可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
    关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
    def person(name, age, **kw):
        print('name:', name, 'age:', age, 'other:', kw)
    可以传入任意个数的关键字参数
    >>> person('Bob', 35, city='Beijing')
    name: Bob age: 35 other: {'city': 'Beijing'}
    >>> person('Adam', 45, gender='M', job='Engineer')
    name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
    可以扩展函数的功能。
    可以先组装为一个dict,然后传进去。
    >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 24, **extra)
    name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
    **extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。

    命名关键字

    在内部对传入的关键字进行检测。
    def person(name, age, **kw):if 'city' in kw:
            # 有city参数passif 'job' in kw:
            # 有job参数pass
        print('name:', name, 'age:', age, 'other:', kw)
    限制关键字参数的名字,可以用命名关键字。
    def person(name, age, *, city, job):
        print(name, age, city, job)
    如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
    def person(name, age, *args, city, job):
        print(name, age, args, city, job)

    参数组合


    参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
    def f1(a, b, c=0, *args, **kw):
        print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
    
    def f2(a, b, c=0, *, d, **kw):
        print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
    >>> f1(1, 2)
    a = 1 b = 2 c = 0 args = () kw = {}
    >>> f1(1, 2, c=3)
    a = 1 b = 2 c = 3 args = () kw = {}
    >>> f1(1, 2, 3, 'a', 'b')
    a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
    >>> f1(1, 2, 3, 'a', 'b', x=99)
    a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
    >>> f2(1, 2, d=99, ext=None)
    a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}


    通过一个tuple和dict,你也可以调用上述函数:
    >>> args = (1, 2, 3, 4)
    >>> kw = {'d': 99, 'x': '#'}
    >>> f1(*args, **kw)
    a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
    >>> args = (1, 2, 3)
    >>> kw = {'d': 88, 'x': '#'}
    >>> f2(*args, **kw)
    a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
    对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

    小结


    默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
     
    要注意定义可变参数和关键字参数的语法:
     
    *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})。

    命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。 
    定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。

    递归函数

    def fact(n):if n==1:
            return 1return n * fact(n - 1)

    防止栈溢出。
    通过尾递归优化。和循环的效果一样。在函数返回的时候,调用自身。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)
    可以看到,return fact_iter(num - 1, num * product)仅返回递归函数本身,num - 1和num * product在函数调用前就会被计算,不影响函数调用。

    高级特性


    构造列表:
    1. L = []
    2. n = 1
    3. while n <= 99:
    4. L.append(n)
    5. n = n + 2

    切片

    取前N个元素。
    >>> r = []
    >>> n = 3>>> for i in range(n):
    ...     r.append(L[i])
    ... 

    >>> L[0:3]
    >>> L[-2:]
    记住倒数第一个元素的索引是-1
    前11-20个数:
    >>> L[10:20]
    前10个数,每两个取一个:
    >>> L[:10:2]
    所有数每5个取一个
    >>> L[::5]
    复制应该 list
    >>> L[:]
    tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:
    >>> (0, 1, 2, 3, 4, 5)[:3]
    字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串
    >>> 'ABCDEFG'[:3]
    'ABC'>>> 'ABCDEFG'[::2]
    'ACEG'
    针对字符串提供了很多各种截取函数(例如,substring),其实目的就是对字符串切片。Python没有针对字符串的截取函数,只需要切片一个操作就可以完成,非常简单。

    迭代


    迭代:遍历list or tuple
    Python中迭代是通过for in 实现的,java是通过下标实现的。
    >>> d = {'a': 1, 'b': 2, 'c': 3}
    >>> for key in d:
    ...     print(key)
    ...
    默认dict迭代的是key
    如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。
    由于字符串也是可迭代对象,因此,也可以作用于for循环:
    如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:

    >>> from collections import Iterable
    >>> isinstance('abc', Iterable) # str是否可迭代True>>> isinstance([1,2,3], Iterable) # list是否可迭代True>>> isinstance(123, Iterable) # 整数是否可迭代False
    如果要实现类似Java那样的下标循环。
    Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
    >>> for i, value in enumerate(['A', 'B', 'C']):
    ...     print(i, value)
    ...
    0 A
    1 B
    2 C
    上面的for循环里,同时引用了两个变量,在Python里是很常见的
    1. >>> for x, y in [(1, 1), (2, 4), (3, 9)]:
    2. ... print(x, y)
    3. ...
    4. 1 1
    5. 2 4
    6. 3 9

    列表生成式

    List Comprehensions :内置的,用来创建list的生成式。
    >>> list(range(1, 11))
    如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做?
    >>> [x * x for x in range(1, 11)]
    for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
    1. [x * x for x in range (1 , 11) if x %2 = 0]
    还可以使用两层循环,可以生成全排列:
    1. [m + n for m in 'ABC' for n in 'abc']
    列出当前目录下的文件和目录
    1. import os
    2. [d for d in os.listdir ('.')]
    for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value:
    >>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
    >>> for k, v in d.items():
    ...     print(k, '=', v)
    ...
    y = B
    x = A
    z = C
    1. d = {'x' : 'A' , 'y' : 'B' , 'z' : 'C'}
    2. [k + '=' + v for k,v in d.items()]
    最后把list的字符串变为小写。
    L = ['Hello', 'World', 'IBM', 'Apple']
    1. [s.lower() for s in L]

    1. >>> L = ['Hello', 'World', 18, 'Apple', None]
    2. >>> [s.lower() for s in L]
    3. Traceback (most recent call last):
    4. File "<stdin>", line 1, in <module>
    5. File "<stdin>", line 1, in <listcomp>
    6. AttributeError: 'int' object has no attribute 'lower'
    如果list中既包含字符串,又包含整数,由于非字符串类型没有lower()方法,所以列表生成式会报错:
    使用内建的isinstance函数可以判断一个变量是不是字符串:

    isinstance(x, str)

    1. L2 = [s.lower() for s in L1 if isinstance (s , str)]

    生成器


    列表容量是有限的。
    列表的元素按照算法推算出
    generator:一边循环一边计算
    把列表表达式中的[]变为()
    打印:next ()
    也可以使用循环。
    1. g = (x * x for x in range (10))
    2. for i in g
    3. print (i)

    如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
    1. def fib (max)
    2. n , a , b = 0 , 0 , 1
    3. while n < max :
    4. print(b)
    5. a , b = b , a + b
    6. n = n + 1
    7. return 'done'
    a , b = b  , a + b 
    相当于
    1. t = (b , a + b)
    2. a = t[0]
    3. b = t[1]
    要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
    fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
    >>> f = fib(6)
    >>> f
    <generator object fib at 0x104feaaa0>
    最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回

    而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
    但是用for循环调用generator时,发现拿不到generator的return语句的返回值,因为每次在yield处就中断了。

    如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

    1. >>> g = fib(6)
    2. >>> while True:
    3. ... try:
    4. ... x = next(g)
    5. ... print('g:', x)
    6. ... except StopIteration as e:
    7. ... print('Generator return value:', e.value)
    8. ... break
    9. ...
    普通函数调用直接返回结果:
    generator函数的“调用”实际返回一个generator对象:

    迭代器

    可以作用于for循环的主要有如下两种:
    •  集合数据类型:list,tuple,dict , set , str 
    • generator : 生成器和带yield的generate function

    可以直接作用于for循环的对象,统称为可迭代对象:Iterable
    可以使用isinstance()判断一个对象是否是Iterable对象:

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

    可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
    把list、dict、str等Iterable变成Iterator可以使用iter()函数:
    >>> isinstance(iter([]), Iterator)
    True
    为什么list、dict、str等数据类型不是Iterator?

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

    小结

    凡是可作用于for循环的对象都是Iterable类型; 
    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列; 
    集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
    for循环本质上就是通过不断调用next()函数实现的
    1. # 首先获得Iterator对象:
    2. it = iter([1, 2, 3, 4, 5])
    3. # 循环:
    4. while True:
    5. try:
    6. # 获得下一个值:
    7. x = next(it)
    8. except StopIteration:
    9. # 遇到StopIteration就退出循环
    10. break

    函数式编程

    而函数式编程(请注意多了一个“式”字)——Functional Programming,

    在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。
    而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。
    对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。

    高阶函数

    Higher-order function

    变量可以指向函数

    abs(-10)是函数调用,而abs是函数本身。
    f = abs
    结论:函数本身也可以赋值给变量,即:变量可以指向函数。
    >>> f = abs
    >>> f(-10)
    10
    直接调用abs()函数和调用变量f()完全相同。

    函数名也是变量

    对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数! 
    如果把abs指向其他对象,会有什么情况发生?
    >>> abs = 10
    >>> abs(-10)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'int' object is not callable
    要恢复abs函数,请重启Python交互环境。
     
    注:由于abs函数实际上是定义在import builtins模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10。

    传入函数


    那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
    def add(x, y, f):return f(x) + f(y)
    add(-5, 6, abs)

    x = -5
    y = 6
    f = abs
    f(x) + f(y) ==> abs(-5) + abs(6) ==> 11return 11

    map/reduce


    map()函数接收两个参数,一个是函数,一个是Iterable,
    map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
    我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:


    >>> 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]
    由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。
    map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数
    >>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
    ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
    reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
    序列求和:
    >>> from functools import reduce
    >>> def add(x, y):...     return x + y
    ...
    >>> reduce(add, [1, 3, 5, 7, 9])
    25
    如果要把序列[1, 3, 5, 7, 9]变换成整数13579,reduce就可以派上用场:
    >>> from functools import reduce
    >>> def fn(x, y):
    ...     return x * 10 + y
    ...
    >>> reduce(fn, [1, 3, 5, 7, 9])
    13579
    果考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数:
    1. >>> from functools import reduce
    2. >>> def fn(x, y):
    3. ... return x * 10 + y
    4. ...
    5. >>> def char2num(s):
    6. ... return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    7. ...
    8. >>> reduce(fn, map(char2num, '13579'))
    9. 13579
    整理成一个str2int的函数就是:
    1. from functools import reduce
    2. def str2int(s):
    3. def fn(x, y):
    4. return x * 10 + y
    5. def char2num(s):
    6. return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    7. return reduce(fn, map(char2num, s))
    还可以用lambda函数进一步简化成:
    1. from functools import reduce
    2. def char2num(s):
    3. return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    4. def str2int(s):
    5. return reduce(lambda x, y: x * 10 + y, map(char2num, s))
    1. from functools import reduce
    2. def str2float(L):
    3. dotIndex=L.find('.')
    4. times=len(L)-dotIndex-1
    5. s=L.replace('.','')
    6. def char2num(s):
    7. return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    8. def fn(x, y):
    9. return x * 10 + y
    10. L=reduce(fn, map(char2num, s))
    11. print ('times = ', pow(10,times))
    12. return L/pow(10 , times)
    13. print(str2float('123.456'))

    filter

    过滤序列。
    接收一个序列。
    把传入的函数作用于每个元素,根据返回值是ture和False决定保留还是丢弃。
    删除偶数,保留奇数。
    def is_odd(n):return n % 2 == 1
    
    list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
    # 结果: [1, 5, 9, 15]
    如果为false,则删除。
    s.strip(rm)        删除s字符串中开头、结尾处,位于 rm删除序列的字符
    当rm为空时,默认删除空白符(包括'\n', '\r',  '\t',  ' ')
    把空字符串删掉。
    def not_empty(s):return s and s.strip()
    
    list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
    # 结果: ['A', 'B', 'C']
    注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

    用filter求素数

    埃氏筛法
    首先,列出从2开始的所有自然数,构造一个序列:
    取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉:
    取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉:
    取新序列的第一个数5,然后用5把序列的5的倍数筛掉:
    构造从3开始的奇数序列
    def _odd_iter():
        n = 1while True:
            n = n + 2yield n
    注意这是一个生成器,并且是一个无限序列。
    定义一个筛选函数:
    1. def _not_divisible(n):
    2. return lambda x: x % n > 0
    最后,定义一个生成器,不断返回下一个素数:
    1. def primes():
    2. yield 2
    3. it = _odd_iter() # 初始序列
    4. while True:
    5. n = next(it) # 返回序列的第一个数
    6. yield n
    7. it = filter(_not_divisible(n), it) # 构造新序列
    这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列。 
    由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件:
    1. for n in primes():
    2. if n < 1000:
    3. print(n)
    4. else:
    5. break
    注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁。
    filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。

    sorted


    高阶函数:sorted([36, 5, -12, 9, -21], key=abs)
    默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。

    忽略大小写:只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写)
    >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
    反向排序:
     sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
    sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。

    返回函数

    函数作为返回值。

    1. def calc_sum(*args):
    2. ax = 0
    3. for n in args:
    4. ax = ax + n
    5. return ax
    如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:

    1. def lazy_sum(*args):
    2. def sum():
    3. ax = 0
    4. for n in args:
    5. ax = ax + n
    6. return ax
    7. return sum
    当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
    >>> f = lazy_sum(1, 3, 5, 7, 9)
    调用函数f时,才真正计算求和的结果:

    在这个例子中,我们在函数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

    闭包

    注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易
    返回的函数并没有立刻执行,而是直到调用了f()才执行。

    另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行
    1. def count():
    2. fs = []
    3. for i in range(1, 4):
    4. def f():
    5. return i*i
    6. fs.append(f)
    7. return fs
    8. f1, f2, f3 = count()
    每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。 
    你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
    全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
     
    返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
     
    如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:


    1. def count():
    2. def f(j):
    3. def g():
    4. return j*j
    5. return g
    6. fs = []
    7. for i in range(1, 4):
    8. fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    9. return fs

    返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

    匿名函数

    >>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
    匿名函数lambda x: x * x实际上就是:
    1. def f(x):
    2. return x * x
    关键字lambda表示匿名函数,冒号前面的x表示函数参数。
    匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
    此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

    装饰器

    函数对象有一个__name__属性,可以拿到函数的名字:
    假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

    decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
    1. def log(func):
    2. def wrapper(*args, **kw):
    3. print('call %s():' % func.__name__)
    4. return func(*args, **kw)
    5. return wrapper
    观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

    1. @log
    2. def now():
    3. print('2015-3-25')
    把@log放到now()函数的定义处,相当于执行了语句: 
    now = log(now)
    由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

    wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

    如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

    1. def log(text):
    2. def decorator(func):
    3. def wrapper(*args, **kw):
    4. print('%s %s():' % (text, func.__name__))
    5. return func(*args, **kw)
    6. return wrapper
    7. return decorator
    1. @log('execute')
    2. def now():
    3. print('2015-3-25')

    偏函数


    Partial function
    int('12345', base=8)
    可以定义一个int2()的函数,默认把base=2传进去:
    functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
    1. >>> import functools
    2. >>> int2 = functools.partial(int, base=2)
    3. >>> int2('1000000')
    4. 64
    5. >>> int2('1010101')
    6. 85
    相当于
    kw = { 'base': 2 }
    int('10010', **kw)
    max2 = functools.partial(max, 10)
    实际上会把10作为*args的一部分自动加到左边,也就是
    max2(5, 6, 7)
    相当于
    1. args = (10, 5, 6, 7)
    2. max(*args)

    模块


    我们把很多函数分组,分别放到不同的文件里面。
    自己在编写模块时,不必考虑名字会与其他模块冲突
    如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。
    方法是选择一个顶层包名,比如mycompany,
    每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是mycompany。

    使用模块

    Mac或Linux上有可能并存Python 3.x和Python 2.x,因此对应的pip命令是pip3。
    确保安装时勾选了pip和Add python.exe to Path。
    pip install Pillow
    生成缩略图
    1. >>> from PIL import Image
    2. >>> im = Image.open('test.png')
    3. >>> print(im.format, im.size, im.mode)
    4. PNG (400, 300) RGB
    5. >>> im.thumbnail((200, 100))
    6. >>> im.save('thumb.jpg', 'JPEG')
    其他常用的第三方库还有MySQL的驱动:mysql-connector-python,用于科学计算的NumPy库:numpy,用于生成文本的模板工具Jinja2,等等。

    当我们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件
    Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:
    >>> import sys
    >>> sys.path
    添加自己的目录:
    • 修改sys.path
    • 第二种方法是设置环境变量PYTHONPATH

    面向对象编程

    class Student(object):pass
    class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
    1. bart = Student()
     注意:特殊方法“init”前后有两个下划线!!!
    1. class Student(object):
    2. def __init__(self, name, score):
    3. self.name = name
    4. self.score = score
    注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
     
    有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:
    和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数

    访问限制

    内部属性不被外部访问。把属性的名称前加上两个下划线__,
    原先那种直接通过bart.score = 59也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数:

    继承和多态

    新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
    1. class Cat(Animal):

    继承原来的代码,可以做一定改进。
    子类的run()覆盖了父类的run()
    多态:
    因为Dog是从Animal继承下来的,当我们创建了一个Dog的实例c时,我们认为c的数据类型是Dog没错,但c同时也是Animal也没错,Dog本来就是Animal的一种!

    当我们定义一个class的时候,我们实际上就定义了一种数据类型
    在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:
    多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:

    对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

    对扩展开放:允许新增Animal子类; 
    对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

    静态语言VS动态语言

    对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
     
    对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

    这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子

    获取对象信息


    使用type()

    果我们要在if语句中判断,就需要比较两个变量的type类型是否相同:

    1. >>> type(fn)==types.FunctionType
    2. True
    3. >>> type(abs)==types.BuiltinFunctionType
    4. True
    5. >>> type(lambda x: x)==types.LambdaType
    6. True
    7. >>> type((x for x in range(10)))==types.GeneratorType
    8. True

    isinstance()

    object -> Animal -> Dog -> Husky
    >>> a = Animal()
    >>> d = Dog()
    >>> h = Husky()
    isinstance(h, Husky)
    >>> isinstance(h, Animal)
    True

    dir()

    如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,

    比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:
    1. >>> len('ABC')
    2. 3
    3. >>> 'ABC'.__len__()
    4. 3
    仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:

    我们可以对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,我们才会去获取对象信息
    可以用
    sum = obj.x + obj.y

    就不要写:

    sum = getattr(obj, 'x') + getattr(obj, 'y')
    假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。hasattr()就派上了用场。

    实例属性和类属性

    1. >>> class Student(object):
    2. ... name = 'Student'
    3. ...
    4. >>> s = Student() # 创建实例s
    5. >>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
    6. Student
    7. >>> print(Student.name) # 打印类的name属性
    8. Student
    9. >>> s.name = 'Michael' # 给实例绑定name属性
    10. >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
    11. Michael
    12. >>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
    13. Student
    14. >>> del s.name # 如果删除实例的name属性
    15. >>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
    16. Student

    从上面的例子可以看出,在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。


    使用__slots__

    1. class Student(object):
    2. pass
    1. >>> s = Student()
    2. >>> s.name = 'Michael' # 动态给实例绑定一个属性
    3. >>> print(s.name)
    4. Michael
    但是,给一个实例绑定的方法,对另一个实例是不起作用的:
    为了给所有实例都绑定方法,可以给class绑定方法:
    通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。
    但是如果想限制实例的属性
    定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

    1. class Student(object):
    2. __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:

    除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。

    访问数据库


    在Mac或Linux上,需要编辑MySQL的配置文件,把数据库默认的编码全部改为UTF-8。MySQL的配置文件默认存放在/etc/my.cnf或者/etc/mysql/my.cnf:
    1. [client]
    2. default-character-set = utf8
    3. [mysqld]
    4. default-storage-engine = INNODB
    5. character-set-server = utf8
    6. collation-server = utf8_general_ci
    mysql> show variables like '%char%';
    安装驱动:
     pip install mysql-connector
    1. # 导入MySQL驱动:
    2. >>> import mysql.connector
    3. # 注意把password设为你的root口令:
    4. >>> conn = mysql.connector.connect(user='root', password='password', database='test')
    5. >>> cursor = conn.cursor()
    6. # 创建user表:
    7. >>> cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
    8. # 插入一行记录,注意MySQL的占位符是%s:
    9. >>> cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'Michael'])
    10. >>> cursor.rowcount
    11. 1
    12. # 提交事务:
    13. >>> conn.commit()
    14. >>> cursor.close()
    15. # 运行查询:
    16. >>> cursor = conn.cursor()
    17. >>> cursor.execute('select * from user where id = %s', ('1',))
    18. >>> values = cursor.fetchall()
    19. >>> values
    20. [('1', 'Michael')]
    21. # 关闭Cursor和Connection:
    22. >>> cursor.close()
    23. True
    24. >>> conn.close()






















    附件列表

      也可以参见简书主页:https://www.jianshu.com/u/482f183ec380
    • 相关阅读:
      SpringMvc+hibernate+easyui简单的权限管理系统
      使用Spring Security实现权限管理
      基于Spring Security2与 Ext 的权限管理设计与兑现
      Hibernate里面如何使用DetachedCriteriaCriteria 实现多条件分页查询
      mysql存储过程
      java学习路线与书籍推荐
      你真的懂wait、notify和notifyAll吗
      java中String对象的存储位置
      如何阅读Java源码
      Session概述(java)
    • 原文地址:https://www.cnblogs.com/dy2903/p/7230421.html
    Copyright © 2020-2023  润新知