您的位置:新葡亰496net > 电脑系统 > 新葡亰496net:基础衍生,高级编程

新葡亰496net:基础衍生,高级编程

发布时间:2019-11-24 07:13编辑:电脑系统浏览(100)

     

    1.1==,is的使用

    切片

    切片操作可以快速地选取listtuplestr的部分元素。

    例如,l[x:y],表示截取l[x,y)索引范围的值所得的列表。

    >>> l = ['Tim', 'Trump', 'Obama', 'Ivanka', 'Jane']
    >>> l
    ['Tim', 'Trump', 'Obama', 'Ivanka', 'Jane']
    >>> l[0:3]
    ['Tim', 'Trump', 'Obama']
    >>> l[:3]
    ['Tim', 'Trump', 'Obama']
    >>> l[1:3]
    ['Trump', 'Obama']
    >>> l[-2:]
    ['Ivanka', 'Jane']
    >>> l[-2:-1]
    ['Ivanka']
    >>> l = list(range(100))
    >>> l
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
    >>> l[:10]
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> l[-10:]
    [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
    >>> l[10:20]
    [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    >>> l[:10:2]
    [0, 2, 4, 6, 8]
    >>> l[::5]
    [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
    >>> l1 = l[:]
    >>> l1
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
    >>> l1 == l
    True
    >>> l1 is l
    False
    >>> id(l1)
    4325251144
    >>> id(l)
    4325252552
    

    id()返回变量的唯一标识,CPython则是返回指向的对象的内存地址。

    • ==判断变量指向的对象的值是否相等
    • is判断变量的唯一标识是否相等,CPython则是指向的对象的内存地址是否相等。

    注意,[:]可以快速的复制一个列表,并且两个变量的地址是不同的。

    tuple切片返回的依然是tuple

    str切片返回的依然是str

    >>> (1, 2, 3, 4, 5)[3:]
    (4, 5)
    >>> 'abcdefg'[2:4]
    'cd'
    

    1.1==,is的使用

    生成器
    生成器就是一边循环一边执行可以减小内存压力
    生成器的两种方法
    第一种 将列表生成式进行修改 中括号改成小括号就好了
    第二种 就是循环函数中添加 yield
    生成器获取 方式 next() __next()
    yield 有send () 给一个数据 本身yield 代码块没有返回值

    本文适应人群:C# or Python3 基础巩固

    新葡亰496net 1

    迭代

    使用for ... in来遍历listtuplestrdict可迭代对象

    CJava等语言不一样,python并不要求对象需要有下标。比如dict,注意dict是无序的。

    字典默认遍历的是key,遍历value使用for v in d.values(),同时迭代key和value使用for k, v in d.items()

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    l = [1, 2, 3, 4, 5]
    for x in l:
        print(x)
    
    t = ('1', 'hello', 2.3)
    for x in t:
        print(x)
    
    s = 'hello'
    for ch in s:
        print(ch)
    
    d = {'name': 'Tim', 'age': 22, 'city': 'bj'}
    for k in d:
        print(k)
    
    for v in d.values():
        print(v)
    
    for k, v in d.items():
        print(k, v)
    

    如果要打印可迭代对象的下标,使用内置的enumerate(obj)转成枚举对象。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    l = [1, 2, 3, 4, 5]
    for i, x in enumerate(l):
        print(i, x)
    
    t = ('1', 'hello', 2.3)
    for i, x in enumerate(t):
        print(i, x)
    
    s = 'hello'
    for i, ch in enumerate(s):
        print(i, ch)
    
    d = {'name': 'Tim', 'age': 22, 'city': 'bj'}
    for i, k in enumerate(d):
        print(i, k)
    
    for i, v in enumerate(d.values()):
        print(i, v)
    
    for i, t in enumerate(d.items()):   # t是tuple
        print(i, t, t[0], t[1])
    

    在python中,for循环里面引用两个变量很常见:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    for x, y in [(0, 0), (12, 90), (2, 9)]:
        print(x, y)
    

    ·is是比较两个引用是否指向了同一个对象(引用比较)。

    迭代器
    可迭代对象 iterable
    集合数据类型 生成器
    迭代器 iterator
    可以用next获取的数据
    如何判断
    from collections import Iterable,Iterator
    通过isinstance进行判读
    iter 可以将可迭代对象转成 迭代器

    代码裤子:

    新葡亰496net 2

    可迭代对象

    要判断一个对象是可迭代对象,通过collections模块的Iterable类型判断。

    >>> from collections import Iterable
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance((), Iterable)
    True
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance(100, Iterable)
    False
    

    ·==是比较两个对象是否相等。

    闭包
    就是函数里面嵌套函数形成闭包 闭包的范围是内部函数和变量
    内部变量如何在内部函数修改 通过nonlocal 定义 用法和global差不多

    在线编程:

    总结

    列表生成式

    更多用法参考:List Comprehensions

    列表生成式(List Comprehensions),是简洁却强大的创建list的生成式。

    for后面还可以接上if。还可以嵌套(>=2层)循环,比如生成全排列。

    >>> [x * x for x in range(1, 11)]
    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
    >>> [x * x for x in range(1, 11) if x % 2 == 0]
    [4, 16, 36, 64, 100]
    >>> [m   n for m in 'abc' for n in 'xyz']
    ['ax', 'ay', 'az', 'bx', 'by', 'bz', 'cx', 'cy', 'cz']
    

    列出当前目录的文件和目录(不递归)。

    >>> import os
    >>> [d for d in os.listdir('.')]
    ['.DS_Store', 'dir1', 'hellp.py']
    

    列表表达式里面的for也可以使用>=2个变量,比如遍历dict:

    >>> d = {'name': 'Tim', 'age': 22}
    >>> [str(k)   ' => '   str(v) for k, v in d.items()]
    ['name => Tim', 'age => 22']
    

    在列表生成式里面做点函数运算。

    >>> l = ['Tim', 'Trump', 'Obama', 'Ivanka', 'Jane']
    >>> [s.lower() for s in l]
    ['tim', 'trump', 'obama', 'ivanka', 'jane']
    

    1.2深拷贝、浅拷贝

    装饰器
    作用就是在原有功能模块代码基础上不修改原有代码 进行扩展
    原理就是闭包调用一个函数 返回一个新的函数 这个函数的引用 和原函数的引用名一样
    用法
    @ 扩展的函数
    有很多类型可以放参数等等多次装饰

    在线预览:http://github.lesschina.com/python/base/ext/基础衍生.html

    ·is是比较两个引用是否指向了同一个对象(引用比较)。

    生成器

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

    所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(generator)。

    创建一个生成器有几种方法。

    第一种:把列表生成式的[]改成()就好了。

    然后持续调用next(<generator>)输出每个元素。当没有更多元素,报StopIteration错。

    >>> g = (x * x for x in range(5))
    >>> g
    <generator object <genexpr> at 0x101f4d728>
    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
    >>> next(g)
    9
    >>> next(g)
    16
    >>> next(g)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    事实上,我们永远都不会直接调用next(<generator>)去输出元素,而是使用for循环。并且也不会报StopIteration错。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    g = (x * x for x in range(5))
    for x in g:
        print(x)
    

    执行结果:

    1
    4
    9
    16
    

    或者使用列表生成式输出。

    >>> g = (x * x for x in range(5))
    >>> g
    <generator object <genexpr> at 0x101cdf570>
    >>> [x for x in g]
    [0, 1, 4, 9, 16]
    >>> [i for i in (x * x for x in range(5))]
    [0, 1, 4, 9, 16]
    

    可以看到,生成器用来保存算法。当推算的算法比较复杂,使用列表生成式这样的for循环无法实现时,还可以使用函数来实现。

    比如斐波那契数列。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    def fib(n):
        a, b = 0, 1
        while n > 0:
            print(a)
            a, b = b, a   b
            n -= 1
    
    fib(10)
    

    创建生成器的第二种方式就是使用函数。当函数中有yield关键字,调用这个函数后的返回值就是一个生成器。这个函数也可以有return语句等关键字。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    def fib(n):
        a, b = 0, 1
        while n > 0:
            yield a
            a, b = b, a   b
            n -= 1
    
    f = fib
    print(type(f))
    print(f)
    g = fib(10)
    print(type(g))
    print(g)
    print([x for x in g])
    

    执行结果:

    <class 'function'>
    <function fib at 0x10df42f28>
    <class 'generator'>
    <generator object fib at 0x10e174150>
    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
    

    这类生成器执行流程:在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

    下面的例子的运行结果证明上面的说法。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    def odd():
        print('step1')
        yield 1
        print('step2')
        yield 3
        print('step3')
        yield 5
    
    o = odd()
    print(next(o))
    print(next(o))
    print(next(o))
    print(next(o))
    

    执行结果:

    step1
    1
    step2
    3
    step3
    5
    Traceback (most recent call last):
      File "./hellp.py", line 16, in <module>
        print(next(o))
    StopIteration
    

    可以看到,生成器遇到yield就中断,不会继续执行,直到下次调用next()。因此生成器需要设置合适的条件来终止生成器不停地next()

    看下面的例子:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    def fun1():
        yield 1
        yield 2
        yield 3
        return 'Done'
    
    g = fun1()
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    

    执行结果:

    1
    2
    3
    Traceback (most recent call last):
      File "./hellp.py", line 13, in <module>
        print(next(g))
    StopIteration: Done
    

    可以看到,报StopIteration错时打印了return语句的返回值。

    当我们使用for循环遍历时,是获取不到的。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    def fun1():
        yield 1
        yield 2
        yield 3
        return 'Done'
    
    for x in fun1():
        print(x)
    

    执行结果:

    1
    2
    3
    

    要捕获return的返回值,不能使用for循环,只能调用next(),此时需要异常捕获。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    def fun1():
        yield 1
        yield 2
        yield 3
        return 'Done'
    
    g = fun1()
    while True:
        try:
            print(next(g))
        except StopIteration as e:
            print(e.value)
            break
    

    执行结果:

    1
    2
    3
    Done
    

    下面的例子用于打印杨辉三角,注意临时列表tmp必须拷贝res一份,否则tmp也指向res指向的对象,这样就不是杨辉三角的上一层列表了!

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    def triangle(n):
        tmp = []
        res = []
        while n >= 0:
            res.append(1)
            for i in range(len(tmp)-1):     # range(x),x<=0是没有输出的
                res[i   1] = tmp[i]   tmp[i   1]
            tmp = res[:]    # 必须拷贝一份,否则指向同一个对象!
            n -= 1
            yield res
    
    g = triangle(5)
    for x in g:
        print(x)
    

    执行结果:

    [1]
    [1, 1]
    [1, 2, 1]
    [1, 3, 3, 1]
    [1, 4, 6, 4, 1]
    [1, 5, 10, 10, 5, 1]
    

    1.2.1浅拷贝

    import 模块的导入 sys.path 可以查看 模块导入的搜索路径是个列表可以进行列表操作
    重新加载模块 通过 from imp import reload reload(模块名)
    进行重新加载

    马上快期末考试了,老师蜜月也回来了,于是有了一场跨季度的复习讲课了:

    ·==是比较两个对象是否相等。

    迭代器

    浅拷贝是对于一个对象的顶层拷贝

    作用域 命令空间

    1.Python基础语法扩展¶

    1.1深拷贝、浅拷贝

    可迭代对象

    能直接作用于for循环的类型称之为可迭代对象,即Iterable。有以下2类:

    • 集合数据类型:listtuplestrdictset等。
    • 生成器generator:生成器和带yield的生成器函数。

    使用isinstance(obj, type)判断对象是否是可迭代对象Iterable,需要从collections导入Iterable

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    >>> isinstance((), Iterable)
    True
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance(set([1, 2, 3]), Iterable)
    True
    >>> isinstance((x for x in range(5)), Iterable)
    True
    >>> isinstance(100, Iterable)
    False
    

    通俗的理解是:拷贝了引用,并没有拷贝内容

    变量都有各自的作用域 变量的搜索 按照LEGB
    locals 本地变量
    enclosing function 闭包呢的变量
    globals 模块中的全局变量
    builtins 內建属性或方法

    1.1.if 判断条件相关¶

    None、""、0、[]、{} ==> 假

    1、" "、[None,""]、{"":None} ==> 真

    小明可高兴了,前几天被打击的面目全非,这几天老师回来了,又可以大发神威了,于是抢先提交demo:

    In [1]:

    # None
    if None:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [2]:

    # 0为False
    if 0:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [3]:

    # 空字符串
    if "":
        print(True)
    else:
        print(False)
    

     

    False
    

    In [4]:

    # 空列表为False
    if []:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [5]:

    # 空字典为False
    if {}:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [6]:

    # 1为True
    if 1:
        print(True)
    else:
        print(False)
    

     

    True
    

    In [7]:

    # 含空格
    if " ":
        print(True)
    else:
        print(False)
    

     

    True
    

    In [8]:

    if [None,""]:
        print(True)
    else:
        print(False)
    

     

    True
    

    In [9]:

    if {"":None}:
        print(True)
    else:
        print(False)
    

     

    True
    

     

    老师微带笑容的看了小明一眼,然后接着讲if的扩展

    1.1.1浅拷贝

    迭代器

    其中,生成器还可以被next()函数调用获取下一个值。

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

    使用isinstance(obj, type)判断对象是否是迭代器Iterator,需要从collections导入Iterator。

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

    可以看到,listtuplestrdictset等都不是迭代器。

    但是,可以通过iter()listtuplestrdictset转为迭代器。

    >>> it = iter([])
    >>> it
    <list_iterator object at 0x10e4536d8>
    >>> type(it)
    <class 'list_iterator'>
    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter(()), Iterator)
    True
    >>> isinstance(iter({}), Iterator)
    True
    >>> isinstance(iter(''), Iterator)
    True
    >>> isinstance(iter(set([])), Iterator)
    True
    

    为什么listtuplestrdictset等数据类型不是Iterator?

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

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

    1.2.2深拷贝

    == is == 值相同 is 是内存地址相同

    1.2.三元表达符¶

    eg:max = a if a > b else b

    In [10]:

    a, b = 1, 2
    
    max = a if a > b else b
    
    print(max)
    

     

    2
    

    In [11]:

    a, b, c = 1, 3, 2
    
    max = a if a > b else b
    max = max if max > c else c
    
    print(max)
    

     

    3
    

    In [12]:

    # 上面的那个还有一种简写(不推荐)
    a, b, c = 1, 3, 2
    
    max = (a if a > b else b) if (a if a > b else b) > c else c
    
    print(max)
    

     

    3
    

     

    浅拷贝是对于一个对象的顶层拷贝

    copy.deepcopy

    深拷贝 浅拷贝 利用copy模块
    copy.copy 浅拷贝 不可变类型复制引用 可变类型 只会复制顶层数据
    copy.deepcopy 深拷贝 不可变类型 查看 内部是否有可变类型如果有就申请内存拷贝一份 如果没有复制引用 可变类型 将所有层都复制一份

    1.2.字符串和编码¶

    Python3.x版本中,字符串是以Unicode编码的

    对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符

    小潘对这块有所研究,把小明按在桌上然后抢先提交demo:

    In [13]:

    ord('D')
    

    Out[13]:

    68
    

    In [14]:

    ord('毒')
    

    Out[14]:

    27602
    

    In [15]:

    chr(68)
    

    Out[15]:

    'D'
    

    In [16]:

    chr(27602)
    

    Out[16]:

    '毒'
    

    In [17]:

    print(ord('A'))
    print(ord('Z'))
    
    print(ord('a'))
    print(ord('z'))
    

     

    65
    90
    97
    122
    

     

    老师补充讲解道:

    编码:encode() 解码:decode()

    url相关的可以用:

    urllib.parse.quote() and urllib.parse.unquote()

    urllib.parse.urlencode() 可以直接对一个key-value进行url编码

    In [18]:

    # encode() and decode()
    name="毒逆天"
    
    name_encode=name.encode("utf-8")
    
    print(name_encode)
    
    print(name_encode.decode("utf-8"))
    

     

    b'xe6xafx92xe9x80x86xe5xa4xa9'
    毒逆天
    

    In [19]:

    # 需要导入urlib.parse
    
    import urllib.parse
    

    In [20]:

    test_str="淡定"
    
    # 对字符串进行url编码和解码
    test_str_enode = urllib.parse.quote(test_str)
    
    print(test_str_enode)
    
    # urllib.parse.quote() 解码
    print(urllib.parse.unquote(test_str_enode))
    

     

    淡定
    淡定
    

    In [21]:

    # urlencode 可以直接对一个key-value进行编码
    
    test_dict={"name":"毒逆天","age":23}
    
    encode_str = urllib.parse.urlencode(test_dict)
    
    print(encode_str)
    print(urllib.parse.unquote(encode_str))
    

     

    name=毒逆天&age=23
    name=毒逆天&age=23
    

     

    通俗的理解是:拷贝了引用,并没有拷贝内容

    1.2.3拷贝的其他方式

    私有化 _x 作用 模块导入时 不会被 导入 _x 类中私有的不会别继承 xx 魔法方法 x 为了区分关键字

    1.3.值判断和地址判断¶

    小明不乐意了,你个小潘总是抢我的风头,看完标题就刷刷的在黑板上写下了如下知识点:

    is 是比较两个引用是否指向了同一个对象id()得到的地址一样则相同)

    == 是比较两个对象的值是否相等

    在之前讲Dict的时候提了一下可变和不可变类型:https://www.cnblogs.com/dotnetcrazy/p/9155310.html#5.2.增加和修改

    Func里面又系统的说了一下:https://www.cnblogs.com/dotnetcrazy/p/9175950.html#4.5.可变类型和不可变类型

    对于可变不可变系列就不去复述了,下面再来几个案例看看 值判断地址判断的概念

    In [22]:

    ################ 可变类型 ################ 
    

    In [23]:

    a=[1,2,3]
    b=[1,2,3]
    
    # id不一样,那is肯定不一样了
    print(id(a))
    print(id(b))
    

     

    139727165899464
    139727165725256
    

    In [24]:

    # a和b是否指向同一个地址
    a is b
    

    Out[24]:

    False
    

    In [25]:

    # a和b的值是否相同
    a == b
    

    Out[25]:

    True
    

    In [26]:

    ################ 开始变化了 ################ 
    

    In [27]:

    # 让a指向b的地址
    a=b
    
    # a和b的id一样了
    print(id(a))
    print(id(b))
    

     

    139727165725256
    139727165725256
    

    In [28]:

    # a和b是否指向同一个地址
    a is b
    

    Out[28]:

    True
    

    In [29]:

    # a和b的值是否相同
    a == b
    

    Out[29]:

    True
    

    In [30]:

    ################ 不可变类型 ################ 
    

    In [31]:

    a=1
    b=1
    
    # id一样
    print(id(a))
    print(id(b))
    

     

    94592578394656
    94592578394656
    

    In [32]:

    a is b
    

    Out[32]:

    True
    

    In [33]:

    a == b
    

    Out[33]:

    True
    

    In [34]:

    # 但是你要注意,不是所有不可变类型都这样的
    
    f1=1.2
    f2=1.2
    
    # 声明两个相同值的浮点型变量,查看它们的id,发现它们并不是指向同个内存地址(这点和int类型不同)
    print(id(f1))
    print(id(f2))
    

     

    139727217917024
    139727217917096
    

    In [35]:

    # 这个就不一样了
    # 这方面涉及Python内存管理机制,Python对int类型和较短的字符串进行了缓存
    # 无论声明多少个值相同的变量,实际上都指向同个内存地址,其他的就没这福利咯~
    
    f1 is f2
    

    Out[35]:

    False
    

    In [36]:

    f1 == f2
    

    Out[36]:

    True
    

     

    新葡亰496net 3

    浅拷贝对不可变类型和可变类型的copy不同

    property 作用 简化 对类中的属性进行修改
    用法 变量=property(get(),set())
    或 通过修饰器 @ property @变量.setter

    2.Python总结之for系列¶

    老师徐徐道来:“之前说for总是零零散散的,现在基础都讲完了,来个小汇总:”

    分析一下以上代码:

    In [88]: a = [11,22,33]

    元类 用来创建类 type方法type(class_name, (father) ,属性和方法{})
    通过metaclass 属性来创建类 也可以自定义

    2.1.Base¶

    能够被for循环遍历的,就是可迭代的

    For基础系:

    In [37]:

    # 类似于for(int i=0;i<5;i  )
    
    for i in range(5):
        print(i)
    

     

    0
    1
    2
    3
    4
    

    In [38]:

    #while循环一般通过数值是否满足来确定循环的条件
    #for循环一般是对能保存多个数据的变量,进行遍历
    
    name="https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ#mmd"
    
    for i in name:
        if i=='#':
            break
        print(i,end='')#另一种写法:print("%s"%i,end="")
    print('n end ...')
    

     

    https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ
     end ...
    

    In [39]:

    # 你期望的结果是:i = 5
    
    for i in range(10):
        if i == 5:
            print("i = %d" % i)
    else:
        print("没有找到")
    

     

    i = 5
    没有找到
    

    In [40]:

    # 当迭代的对象迭代完并为空时,位于else的子句将执行
    # 而如果在for循环中含有break时则直接终止循环,并不会执行else子句
    # 正确写法如下:
    
    for i in range(10):
        if i == 5:
            print("i = %d" % i)
            break
    else:
        print("没有找到")
    

     

    i = 5
    

    In [41]:

    # 遍历一个字典
    
    test_dict={"Name":"小明","Age":23}
    
    for k,v in test_dict.items():
        print("key:%s,value:%s"%(k,v))
    

     

    key:Name,value:小明
    key:Age,value:23
    

     

    a == b  它们的值是一样的。 a is not b  这个判断的是它们的内存地址,它们都是在一个新的内存空间内。在第5行时,又将a,b赋值给c。在第6行时,又将c赋值给d。此时,d的地址等于c的地址,改变a的值,此时 c==d,c is d。 一个‘=’相当于浅复制,相当于引用传递。

    In [89]: b = copy.copy(a)

    垃圾回收
    四个需要注意的点
    小整数池 [-5,256] 内存常驻 系统默认生成
    大整数池 都会重新创建
    inter机制 共享机制 字符串没有空格 多次复制 用的是同一个
    单个字母 内存常驻
    垃圾回收 过程 引用计数为主 分代收集为辅
    分代收集 是为了解决 循环引用问题 有0代 1代 2代
    gc模块
    disable 关闭 垃圾回收
    collect 手动进行一次垃圾回收
    gc.get_threshold 获取引用计数的阀值
    gc.get_count 获取当前的引用计数

    2.2.列表生成式¶

    如果下面知识点还不熟悉的,看看之前讲的~列表生成式:https://www.cnblogs.com/dotnetcrazy/p/9155310.html#1.9.列表生成式

    简写list(range(1, 11)) 全写[x for x in range(1,11)]

    In [42]:

    list(range(1, 11))
    

    Out[42]:

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

    In [43]:

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

    Out[43]:

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

    In [44]:

    # 1~10的平方列表
    [x*x for x in range(1,11)]
    

    Out[44]:

    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
    

    In [45]:

    # 1~10之间的偶数
    [x for x in range(1, 11) if x % 2 == 0]
    

    Out[45]:

    [2, 4, 6, 8, 10]
    

    In [46]:

    # 数学里面的全排列
    [x   y for x in 'ABC' for y in 'AB']
    

    Out[46]:

    ['AA', 'AB', 'BA', 'BB', 'CA', 'CB']
    

    In [47]:

    # 数学里面的坐标轴
    [(x,y) for x in range(1,5) for y in range(1,4)]
    

    Out[47]:

    [(1, 1),
     (1, 2),
     (1, 3),
     (2, 1),
     (2, 2),
     (2, 3),
     (3, 1),
     (3, 2),
     (3, 3),
     (4, 1),
     (4, 2),
     (4, 3)]
    

    In [48]:

    # (x,y,z) 一般三个嵌套就上天了
    [(x,y,z) for x in range(1,5) for y in range(1,4) for z in range(1,3)]
    

    Out[48]:

    [(1, 1, 1),
     (1, 1, 2),
     (1, 2, 1),
     (1, 2, 2),
     (1, 3, 1),
     (1, 3, 2),
     (2, 1, 1),
     (2, 1, 2),
     (2, 2, 1),
     (2, 2, 2),
     (2, 3, 1),
     (2, 3, 2),
     (3, 1, 1),
     (3, 1, 2),
     (3, 2, 1),
     (3, 2, 2),
     (3, 3, 1),
     (3, 3, 2),
     (4, 1, 1),
     (4, 1, 2),
     (4, 2, 1),
     (4, 2, 2),
     (4, 3, 1),
     (4, 3, 2)]
    

     

    1.1.1深拷贝

    In [90]: id(a)

    內键属性 str init del class new 等等

    2.3.扩展¶

    如果要对list实现类似C#或者java那样的下标循环怎么办?

    这块小明又有预习,于是在提交Code的同时大声说道:

    Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身

    In [49]:

    for i, item in enumerate(['A', 'B', 'C']):
        print(i, item)
    

     

    0 A
    1 B
    2 C
    

     

    深拷贝是对于一个对象所有层次的拷贝(递归)

    Out[90]:59275144

    內键函数
    range
    map
    filter
    reduce
    sorted

    3.Python中赋值、浅拷贝、深拷贝¶

    看到标题小明和小潘就楞了,老师当时没讲解啊,然后两个人眼巴巴的看着老师讲解:

    官方文档:

    新葡亰496net 4

    In [91]: id(b)

    集合set
    并集
    交集
    差集
    对称差集

    3.1.赋值¶

    通过=来实现,就是把地址拷贝了一份,比如 a = b

    In [50]:

    a=[1,2,2]
    b = a
    
    print(id(a))
    print(id(b))
    

     

    139727165518536
    139727165518536
    

    In [51]:

    # 再验证
    
    a.append(3)
    
    # 都增加了一个3,说明的确指向同一个内存地址
    print(a)
    print(b)
    

     

    [1, 2, 2, 3]
    [1, 2, 2, 3]
    

     

    新葡亰496net 5

    Out[91]:59525600

    3.2.深拷贝deepcopy¶

    导入copy模块,调用deepcopy方法

    如果有嵌套引用的情况,直接递归拷贝

    In [52]:

    import copy
    
    a=[1,2,2]
    

    In [53]:

    b=copy.deepcopy(a)
    
    # 指向了不同的内存地址
    print(id(a))
    print(id(b))
    

     

    139727165899080
    139727165900488
    

    In [54]:

    # 再验证一下
    
    a.append(3)
    
    # b不变,说明的确指向不同的内存地址
    print(a)
    print(b)
    

     

    [1, 2, 2, 3]
    [1, 2, 2]
    

    In [55]:

    ################ 开始变化了 ################ 
    

    In [56]:

    # 之前讲了嵌套列表,我们来验证一下
    
    a=[1,2,2]
    b=[1,2,3,a]
    
    c=copy.deepcopy(b)
    
    # 发现地址都不一样
    print(id(b))
    print(id(c))
    print(id(b[3]))
    print(id(c[3]))
    

     

    139727166586248
    139727165899080
    139727165725256
    139727165899464
    

    In [57]:

    # 直观的验证一下
    
    a.append(666)
    
    # 深拷贝的确是深拷贝
    print(b)
    print(c)
    

     

    [1, 2, 3, [1, 2, 2, 666]]
    [1, 2, 3, [1, 2, 2]]
    

     

    第九行这里使用了深拷贝,拷贝了c。在a改变值的时候,c也改变了,c和d之前是浅拷贝。e是深拷贝,没有变。地址也和c不一样。

    In [92]: a.append(44)

    3.3.浅拷贝copy¶

    copy只是简单拷贝,如果拷贝内容里面还有引用之类的,他是不管的

    In [58]:

    import copy
    
    a=[1,2,2]
    

    In [59]:

    b=copy.copy(a)
    
    # 指向了不同的内存地址
    print(id(a))
    print(id(b))
    

     

    139727165902088
    139727165850952
    

    In [60]:

    ################ 开始变化了 ################ 
    

    In [61]:

    # 之前讲了嵌套列表,我们来验证一下
    
    a=[1,2,2]
    b=[1,2,3,a]
    
    c=copy.copy(b)
    
    # 第一层地址不一样
    print(id(b))
    print(id(c))
    

     

    139727165519432
    139727165902088
    

    In [62]:

    # 验证一下
    b.append(111)
    
    # 第一层指向的不同地址
    print(b)
    print(c)
    

     

    [1, 2, 3, [1, 2, 2], 111]
    [1, 2, 3, [1, 2, 2]]
    

    In [63]:

    # 如果里面还有引用,那么就不管了
    print(id(b[3]))
    print(id(c[3]))
    

     

    139727165725576
    139727165725576
    

    In [64]:

    # 验证一下
    a.append(666)
    
    # 内部引用的确没copy新地址
    print(b)
    print(c)
    

     

    [1, 2, 3, [1, 2, 2, 666], 111]
    [1, 2, 3, [1, 2, 2, 666]]
    

     

    新葡亰496net 6

    In [93]: a

    3.4.知识扩展¶

    如果拷贝的对象是不可变类型,不管深拷贝和浅拷贝以及赋值都是地址引用

    需要注意的是:Python和Net对于值类型处理是不一样的(管理方式不一样导致的)

    ==>NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝

    In [65]:

    a=(1,2,2)
    b=a
    
    print(id(a))
    print(id(b))
    

     

    139727165526520
    139727165526520
    

    In [66]:

    a=(1,2,2)
    b=copy.deepcopy(a)
    
    print(id(a))
    print(id(b))
    

     

    139727165846872
    139727165846872
    

    In [67]:

    a=(1,2,2)
    b=copy.copy(a)
    
    print(id(a))
    print(id(b))
    

     

    139727165526520
    139727165526520
    

     

    新葡亰496net 7

    Out[93]: [11,22,33,44]

    4.CSharp中赋值、浅拷贝、深拷贝¶

    小明听懂了Python的深拷贝和浅拷贝后,本着学以致用的原则,写下了C#的实现:

    先声明一下,本机环境是Ubuntu NetCore,欢迎贴Code补充

    1.1.1拷贝的其他方式

    In [94]: b

    4.1.赋值¶

    Code:

    赋值方法和Python一样,直接赋值即可

    var list1 = new List<int>() { 1, 2, 2 };
    var list2 = list1;
    

    In [68]:

    %%script csharp
    
    // Python一样,直接赋值即可
    var list1 = new List<int>() { 1, 2, 2 };
    var list2 = list1;
    
    // 验证一下
    list1.Add(3);//我们修改一下list1,list2也就跟着就改变了
    
    foreach (var item in list1)
    {
        Console.Write(item   " ");
    }
    Console.WriteLine();
    foreach (var item in list2)
    {
        Console.Write(item   " ");
    }
    

     

    1 2 2 3 
    1 2 2 3 
    

     

    浅拷贝对不可变类型和可变类型的copy不同

    Out[94]: [11,22,33]

    4.2值类型默认深拷贝¶

    NetCore深拷贝相关的官方文档 public void CopyTo (T[] array);

    简单类型用最简单的方式就能实现深拷贝了:

    官方的CopyTo在这里和这个效果一样,但是比较麻烦,这边就不贴了(Code里面贴了)

    var list3 = new List<int>() { 1, 2, 2 };
    var list4 = new List<int>(list3);
    
    // 验证一下
    list3.Add(3);
    foreach (var item in list3)
    {
        Console.Write(item   " ");
    }
    Console.WriteLine();
    foreach (var item in list4)
    {
        Console.Write(item   " ");
    }
    

    结果:

    1 2 2 3 
    1 2 2
    

    新葡亰496net 8

    In [95]:

    4.3.引用类型默认浅拷贝¶

    对于List<T>再复杂点的,上面的方式就变成浅拷贝了:(类似于Python的Copy.Copy)

    官方的CopyTo在这里和这个效果一样,但是比较麻烦,这边就不贴了(Demo里面贴了)

    定义一个Student

    public partial class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        public override string ToString()
        {
            return $"Name:{Name},Age:{Age}";
        }
    }
    

    浅拷贝Demo:

    var list5 = new List<Student>(){
        new Student { Name = "小张", Age = 22 },
        new Student { Name = "小明", Age = 23 }
        };
    var p = new Student() { Name = "小潘", Age = 23 };
    list5.Add(p);
    
    // 浅拷贝一份
    var list6 = new List<Student>(list5);
    
    // 浅拷贝测试
    // 我们修改一下list5,list6没有跟着改变,说明第一层的地址的确不一样
    list5.Add(new Student() { Name = "小胖", Age = 24 });
    // 当我们修改小潘同学的年龄时,大家都变了,说明真的只是浅拷贝
    p.Age = 24;
    
    foreach (var item in list5)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("=============");
    foreach (var item in list6)
    {
        Console.WriteLine(item);
    }
    

    结果:

    Name:小张,Age:22
    Name:小明,Age:23
    Name:小潘,Age:24
    Name:小胖,Age:24
    =============
    Name:小张,Age:22
    Name:小明,Age:23
    Name:小潘,Age:24
    

     

    新葡亰496net 9

    In [95]:

    4.4.简单方式实现深拷贝¶

    对于List<T>的深拷贝场景,其实项目中还是蛮常见的,那深拷贝怎么搞呢?

    先来一个简单的实现方式,需要T实现ICloneable接口才行:

    定义一个Person类

    public partial class Person : ICloneable
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        //实现ICloneable的Clone方法
        public object Clone()
        {
            return base.MemberwiseClone();//调用父类方法即可
        }
    
        public override string ToString()
        {
            return $"Name:{Name},Age:{Age}";
        }
    }
    

    List<T>定义一个扩展方法:(温馨提醒:扩展方法所在的类必须是static Class哦)

    public static partial class ListExt
    {
        // 只要T实现了ICloneable接口就可以了
        public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> list) where T : ICloneable
        {
            return list.Select(item => (T)item.Clone()).ToList();
        }
    }
    

    来个调用加验证:

    #region 引用类型深拷贝-简单实现方式
    
    var oldList = new List<Person>(){
        new Person(){Name="小明",Age=23},
        new Person(){Name="小张",Age=22},
    };
    var xiaoPan = new Person() { Name = "小潘", Age = 23 };
    oldList.Add(xiaoPan);
    
    var newList = oldList.DeepCopy();
    
    //测试
    oldList.Add(new Person() { Name = "小胖", Age = 23 });
    xiaoPan.Age = 24;
    
    foreach (var item in oldList)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("========");
    foreach (var item in newList)
    {
        Console.WriteLine(item);
    }
    
    #endregion
    

    结果:

    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:24
    Name:小胖,Age:23
    ========
    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:23
    

     

    = :浅拷贝

    In [95]: a = (11,22,33)

    4.5.序列化方式实现深拷贝(常用)¶

    利用System.Runtime.Serialization序列化与反序列化实现深拷贝

    先定义一个Teacher类(别忘记加 Serializable 的标签)

    [Serializable]
    public partial class Teacher
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        public override string ToString()
        {
            return $"Name:{Name},Age:{Age}";
        }
    }
    

    添加一个扩展方法:

    public static partial class ListExt
    {
        // 利用System.Runtime.Serialization序列化与反序列化实现深拷贝
        public static T DeepCopy2<T>(this T obj)
        {
            using (var stream = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
    }
    

    调用:

    #region 引用类型深拷贝-序列化实现
    
    var oldTestList = new List<Teacher>(){
        new Teacher(){Name="小明",Age=23},
        new Teacher(){Name="小张",Age=22},
    };
    var s = new Teacher() { Name = "小潘", Age = 23 };
    oldTestList.Add(s);
    
    var newTestList = oldTestList.DeepCopy2();
    
    //测试
    oldTestList.Add(new Teacher() { Name = "小胖", Age = 23 });
    s.Age = 24;
    
    foreach (var item in oldTestList)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("========");
    foreach (var item in newTestList)
    {
        Console.WriteLine(item);
    }
    
    #endregion
    

    结果:

    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:24
    Name:小胖,Age:23
    ========
    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:23
    

    因为主要是说Python,Net只是简单提一下,这边就先到这里了

    不尽兴可以看看这篇文章,讲得还是挺全面的

    我们接着来对比学习~

     

    copy.deepcopy : 深拷贝

    In [96]: b = copy.copy(a)

    5.Python生成器¶

    一看到标题小明又懵圈了,但是看到大家好像都知道的样子心想道:“我是不是又睡过一节课啊?”

    之前有讲列表生成式,这边说说生成器

    通过列表生成式,我们可以简单并直接的创建一个列表,但是当数据有一定的规律而且又很大的时候,使用列表就有点浪费资源了

    如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,从而节省大量的资源

    copy.copy:  对于列表(深),对于元组(浅)

    In [97]: id(a)

    5.1.简单方式¶

    在Python中,这种一边循环一边计算的机制,称为生成器:generator

    先看一个简单的生成器案例:(只要把一个列表生成式的[]改成() ,就创建了一个generator了)

    In [69]:

    # 列表生成式
    [x for x in range(10)]
    

    Out[69]:

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

    In [70]:

    # 生成器写法(Python2.x系列是用xrange)
    (x for x in range(10))
    

    Out[70]:

    <generator object <genexpr> at 0x7f14c413cb48>
    

     

    遍历方式可以用之前的for循环来遍历(推荐)

    也可以用next()或者__next__()方法来遍历。【C#是用MoveNext

    generator保存的是算法,每次调用next(xxx)或者__next__(),就计算出下一个元素的值,直到计算到最后一个元素

    当没有更多的元素时,抛出StopIteration的异常

    最新的Python3.7在这方面有所优化:

    In [71]:

    g=(x for x in range(10))
    
    # for来遍历(推荐)
    for i in g:
        print(i)
    

     

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    

    In [72]:

    g=(x for x in range(10))
    
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(g.__next__()) #通过__next__也一样取下一个
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    

     

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    

     

    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-72-9897a9148994> in <module>()
         11 print(next(g))
         12 print(next(g))
    ---> 13print(next(g))
         14 print(next(g))
    
    StopIteration: 
    

     

    对于里面的对象相当于浅拷贝

    Out[97]:58890680

    5.2.yield方式¶

    如果推算的算法比较复杂,用类似列表生成式的for循环无法实现时,还可以用函数来实现

    这时候就需要用到yield了,像最经典的斐波拉契数列,这次用一波生成器来对比实现下:

    In [73]:

    # 递归方式:求第30个数是多少
    
    # 1、1、2、3、5、8、13、21、34...
    def fib(n):
        if n == 1 or n == 2:
            return 1
        else:
            return fib(n - 1)   fib(n - 2)
    
    fib(30)
    

    Out[73]:

    832040
    

    In [74]:

    # 在讲yield方式之前先用循环实现一下
    
    def fibona(max):
        n, a, b = 0, 0, 1
        while n < max:
            print(b)
            a, b = b, a   b
            n = n   1
    
    fibona(30)
    

     

    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89
    144
    233
    377
    610
    987
    1597
    2584
    4181
    6765
    10946
    17711
    28657
    46368
    75025
    121393
    196418
    317811
    514229
    832040
    

    In [75]:

    # for循环实现
    
    def fibona(n):
        a, b = 0, 1
        # [0,n)
        for i in range(n):
            print(b)
            a, b = b, a   b
    
    fibona(30)
    

     

    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89
    144
    233
    377
    610
    987
    1597
    2584
    4181
    6765
    10946
    17711
    28657
    46368
    75025
    121393
    196418
    317811
    514229
    832040
    

     

    a, b = b, a b 之前交换两数的时候提过

    这个相当于==>

    temp_tuple = (b, a   b)
    a = temp_tuple[0]
    b = temp_tuple[1]
    

    要把fibona函数变成generator,只需要把print(b)改为yield b就可以了:

    generator在执行过程中,遇到yield就中断,下次又继续执行到yield停下了,一直到最后

    生成器的特点:

    1. 节约内存
    2. 迭代到下一次的调用时,所使用的参数都是第一次所保留下的(所有函数调用的参数都是第一次所调用时保留的,而不是新创建的)

    In [76]:

    # 改成生成器比较简单,直接换输出为yield
    
    def fibona(n):
        a, b = 0, 1
        # [0,n)
        for i in range(n):
            yield b
            a, b = b, a   b
    

    In [77]:

    # 看看是不是生成器
    g = fibona(30)
    
    g
    

    Out[77]:

    <generator object fibona at 0x7f14c40efd58>
    

    In [78]:

    # 遍历输出(基本上都会用for来遍历)
    for i in g:
        print(i)
    

     

    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89
    144
    233
    377
    610
    987
    1597
    2584
    4181
    6765
    10946
    17711
    28657
    46368
    75025
    121393
    196418
    317811
    514229
    832040
    

     

    对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的循环的时候

    小明总结如下:

    1. 在Python中,这种一边循环一边计算的机制称为生成器:generator

    2. 每一个生成器都是一个迭代器(迭代器不一定是生成器)

    3. 如果一个函数包含yield关键字,这个函数就会变为一个生成器

    4. 生成器并不会一次返回所有结果,而是每次遇到yield关键字后返回相应结果,并保留函数当前的运行状态,等待下一次的调用

    5. 由于生成器也是一个迭代器,那么它就支持next用方法来获取下一个值(我们平时用for来遍历它)

    推荐一篇文章,总结的很全了:(yield用法总结)

    1.1属性property

    In [98]: id(b)

    5.3.扩展之~send(msg)方法:¶方法:)

    其实__next__()send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去

    __next__()不 能传递特定的值。我们可以看做x.__next__()x.send(None) 作用是一样的

    In [79]:

    # 来个案例:
    def test_send(n):
        for i in range(n):
            tmp = yield i
            print(tmp)
    
    
    g = test_send(5)
    
    g
    

    Out[79]:

    <generator object test_send at 0x7f14c40efdb0>
    

    In [80]:

    # 定义一个列表
    test_list = []
    
    # 把第一次yield的值放在列表中
    test_list.append(g.__next__())
    
    # 把list传给tmp并打印(可以理解为把表达式右边的 yield i 暂时换成了 test_list)
    # out的内容是yield返回的值
    g.send(test_list)
    

     

    [0]
    

    Out[80]:

    1
    

    In [81]:

    # 以防你们看不懂,来个简单案例
    # 你传啥print(tmp)就给你打印啥
    g.send("你好啊")
    

     

    你好啊
    

    Out[81]:

    2
    

     

    注意一种情况,generator刚启动的时候,要么不传,要么只能传None

    解决:要么一开始send(None)要么一开始先调用一下__next()__ or next()

    In [82]:

    # 注意一种情况,generator刚启动的时候,要么不传,要么只能传None
    def test_send(n):
        for i in range(n):
            tmp = yield i
            print(tmp)
    
    
    g = test_send(5)
    g.send("dog") # TypeError: can't send non-None value to a just-started generator
    

     

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-82-2e891aa5dd81> in <module>()
          7 
          8 g = test_send(5)
    ----> 9g.send("dog") # TypeError: can't send non-None value to a just-started generator
    
    TypeError: can't send non-None value to a just-started generator
    

    In [83]:

    # 解决:要么一开始send(None)要么一开始先调用一下__next()__ or next()
    def test_send(n):
        for i in range(n):
            tmp = yield i
            print(tmp)
    
    
    g = test_send(5)
    g.send(None)
    

    Out[83]:

    0
    

    In [84]:

    g.send("dog")
    

     

    dog
    

    Out[84]:

    1
    

     

    扩:C#在遍历generator的时候也是先调一下MoveNext方法

    while (tmp.MoveNext())
    {
        Console.WriteLine(tmp.Current);
    }
    

     

    面试题:

    Out[98]:58890680

    5.4.扩展之~returnbreak的说明¶

    在一个generator函数中,如果没有return则默认执行至函数完毕

    如果在执行过程中return或者break则直接抛出StopIteration终止迭代

    In [85]:

    # break案例
    def test_send(n):
        for i in range(n):
            if i==2:
                break
            yield i
    
    g = test_send(5)
    for i in g:
        print(i)
    

     

    0
    1
    

    In [86]:

    # return案例
    def test_send(n):
        for i in range(n):
            if i==2:
                return "i==2"
            yield i
    
    g = test_send(5)
    for i in g:
        print(i)
    

     

    0
    1
    

     

    for循环调用generator时,发现拿不到generatorreturn语句的返回值

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

    In [87]:

    # 上面return的返回值怎么拿呢?
    
    g = test_send(5)
    
    while True:
        try:
            tmp = g.__next__()
            print(tmp)
        except StopIteration as ex:
            print(ex.value)
            break # 一定要加break,别忘了你在死循环里呢
    

     

    0
    1
    i==2
    

     

    1、你对面向对象的理解

    ·分片表达式可以赋值一个序列

    5.5.扩展之~协程yield实现多任务调度¶

    这个场景还是很常见的,比如C#的单线程实现多任务用的就可以使用yield

    再比如生产消费这个经典案例:(参考)

    生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产

    Python对协程的支持是通过generator实现的

    在generator中,我们不但可以通过for循环来迭代,还可以不断调用__next__()获取由yield语句返回的下一个值。

    因为Python的yield不但可以返回一个值,它还可以接收调用者发出的参数(通过send方法),所以就happy了

    我们举个简单的demo来看看:

    In [88]:

    def consumer():
        while True:
            tmp = yield
            # !None就变成真了
            if not tmp:
                return
            print("消费者:",tmp)
    

    In [89]:

    # 创建消费者
    c = consumer()
    # 启动消费者
    c.send(None)
    # 生产数据,并提交给消费者
    c.send("小明")
    c.send("小潘")
    # 生产结束,通知消费者结束,抛出StopIteration异常
    c.send(None) # 使用c.close()可以避免异常
    

     

    消费者: 小明
    消费者: 小潘
    

     

    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-89-bcc0083d4089> in <module>()
          7 c.send("小潘")
          8 # 生产结束,通知消费者结束,抛出StopIteration异常
    ----> 9c.send(None) # 使用c.close()可以避免异常
    
    StopIteration: 
    

     

    执行流程

    1. 创建协程对象(消费者)后,必须使用send(None)__next__()启动
    2. 协程在执行yield后让出执行绪,等待消息
    3. 调用方发送send(msg)消息,协程恢复执行,将接收到的数据保存并执行后续流程
    4. 再次循环到yield,协程返回前面的处理结果,并再次让出执行绪
    5. 直到关闭或被引发异常

    补全demo:

    In [90]:

    def consumer():
        status = ""
        while True:
            tmp = yield status
            if not tmp:
                print("消费者已经睡觉了...")
                return
            print("消费者:获得商品%s号..." % tmp)
            status = "ok"
    
    
    def produce(c):
        # 启动消费者
        c.send(None)
        for i in range(1, 3):
            print("生产者:出产商品%s号..." % i)
            # 生产商品,并提交给消费者
            status = c.send(i)
            print("生产者:生产者消费状态: %s" % status)
        # c.send(None) 执行这个会引发StopIteration
        c.close()  # 使用close就可以避免了(手动关闭生成器函数,后面的调用会直接返回StopIteration异常)
    
    
    # 创建消费者
    c = consumer()
    produce(c)
    

     

    生产者:出产商品1号...
    消费者:获得商品1号...
    生产者:生产者消费状态: ok
    生产者:出产商品2号...
    消费者:获得商品2号...
    生产者:生产者消费状态: ok
    

    In [91]:

    # 更多可以查看帮助文档
    def test():
        yield
    help(test())
    

     

    Help on generator object:
    
    test = class generator(object)
     |  Methods defined here:
     |  
     |  __del__(...)
     |  
     |  __getattribute__(self, name, /)
     |      Return getattr(self, name).
     |  
     |  __iter__(self, /)
     |      Implement iter(self).
     |  
     |  __next__(self, /)
     |      Implement next(self).
     |  
     |  __repr__(self, /)
     |      Return repr(self).
     |  
     |  close(...)
     |      close() -> raise GeneratorExit inside generator.
     |  
     |  send(...)
     |      send(arg) -> send 'arg' into generator,
     |      return next yielded value or raise StopIteration.
     |  
     |  throw(...)
     |      throw(typ[,val[,tb]]) -> raise exception in generator,
     |      return next yielded value or raise StopIteration.
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |  
     |  gi_code
     |  
     |  gi_frame
     |  
     |  gi_running
     |  
     |  gi_yieldfrom
     |      object being iterated by yield from, or None
    

     

    2、面向对象的特征是什么

    a ="abc"

    6.Python迭代器¶

    看到迭代器小明老高兴了,心想着一会写个C#版的觉得可以收获一大群眼球~

    3、对封装的理解?

    b = a[:]

    6.1.判断是否可迭代¶

    在说迭代器前先说下可迭代(Iterable)(yield基础点我):

    在Python中,能通过for循环遍历的都是可以迭代的,比如 str、tuple、list、dict、set、生成器等等

    也可以通过 isinstance(xxx,Iterable) 方法判断一下是否迭代:

    In [92]:

    from collections import Iterable
    

    In [93]:

    isinstance("mmd",Iterable)
    

    Out[93]:

    True
    

    In [94]:

    isinstance((1,2),Iterable)
    

    Out[94]:

    True
    

    In [95]:

    isinstance([],Iterable)
    

    Out[95]:

    True
    

    In [96]:

    isinstance({},Iterable)
    

    Out[96]:

    True
    

    In [97]:

    isinstance((x for x in range(10)),Iterable)
    

    Out[97]:

    True
    

    In [98]:

    isinstance(1,Iterable)
    

    Out[98]:

    False
    

     

    封装,类本身就是一个封装,封装了属性和方法。方法也是封装,对一些业务逻辑的封装。私有也是封装,将一些方法和属性私有化,对外提供可访问的接口。

    ·字典的copy方法可以拷贝一个字典

    6.2.判断是否是迭代器¶

    迭代器是一定可以迭代的,怎么判断是迭代器呢?

    可以使用next方法的或者通过isinstance(xxx,Iterator)

    In [99]:

    a=[1,2,3]
    
    next(a)
    

     

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-99-f5f8ac9a8550> in <module>()
          1 a=[1,2,3]
          2 
    ----> 3next(a)
    
    TypeError: 'list' object is not an iterator
    

    In [100]:

    from collections import Iterator
    

    In [101]:

    isinstance([],Iterator)
    

    Out[101]:

    False
    

    In [102]:

    isinstance((x for x in range(10)),Iterator)
    

    Out[102]:

    True
    

     

    4、对继承的理解

    d = dict(name="zhangsan", age=27)

    6.3.IterableIterator

    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator

    list、dict、strIterable变成Iterator可以使用iter()函数:

    In [103]:

    iter(a)
    

    Out[103]:

    <list_iterator at 0x7f14c40a3da0>
    

    In [104]:

    isinstance(iter([]),Iterator)
    

    Out[104]:

    True
    

    In [105]:

    isinstance(iter({}),Iterator)
    

    Out[105]:

    True
    

     

    Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()or__next__()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误

    可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

    Iterator甚至可以表示一个无限大的数据流,而list等则不行

    小明总结了一下老师讲解的知识点:

    1. 可以for循环的对象都是Iterable类型

    2. 可以使用next()or__next__()函数的对象都是Iterator类型

    3. 集合数据类型如list、dict、str等是Iterable,可以通过iter()函数获得一个Iterator对象

     

    将共性的内容放在父类中,子类只需要关注自己特有的内容,共性的继承过来就行了。

    co = d.copy()

    7.CSharp迭代器¶

    乘着下课的时间,小明跑到黑板前,心想:“又到了C#的时候了,看我来收播一大群眼球~”,然后开始了他的个人秀:

    其实迭代器(iterator就是为了更简单的创建枚举器(enumerator)和可枚举类型(enumerator type)的方式

    这样简化开发,符合逻辑习惯,利于扩展。

    ·有些内置函数可以生成拷贝(list)

    7.1.IEnumeratorIEnumerable

    通俗话讲:

    能不能foreach就看你遍历对象有没有实现IEnumerable,就说明你是不是一个可枚举类型enumerator type

    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }
    

    是不是个枚举器(enumerator)就看你实现了IEnumerator接口没

    public interface IEnumerator
    {
        object Current { get; }
    
        bool MoveNext();
    
        void Reset();
    }
    

    最明显的区别:它们两个遍历方式不一样

    // 枚举器遍历
    var tmp = FibonaByIEnumerator(30);
    while (tmp.MoveNext())
    {
        Console.WriteLine(tmp.Current);
    }
    // 可枚举类型遍历
    foreach (var item in FibonaByIEnumerable(30))
    {
        Console.WriteLine(item);
    }
    

    这个我们在2年前就说过,这边简单提一下(官方文档)(Demo)

    MyEnumerator文件:

    public class MyEnumerator : IEnumerator
    {
        /// <summary>
        /// 需要遍历的数组
        /// </summary>
        private string[] array;
        /// <summary>
        /// 有效数的个数
        /// </summary>
        private int count;
        public MyEnumerator(string[] array, int count)
        {
            this.array = array;
            this.count = count;
        }
    
        /// <summary>
        /// 当前索引(线moveNext再获取index,用-1更妥)
        /// </summary>
        private int index = -1;
        public object Current
        {
            get
            {
                return array[index];
            }
        }
        /// <summary>
        /// 移位
        /// </summary>
        /// <returns></returns>
        public bool MoveNext()
        {
            if (  index < count)
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 重置
        /// </summary>
        public void Reset()
        {
            index = -1;
        }
    }
    

    MyArray.cs文件

    public partial class MyArray
    {
        /// <summary>
        /// 数组容量
        /// </summary>
        private string[] array = new string[4];
        /// <summary>
        /// 数组元素个数
        /// </summary>
        private int count = 0;
        /// <summary>
        /// 当前数组的长度
        /// </summary>
        public int Length
        {
            get
            {
                return count;
            }
        }
    
        /// <summary>
        /// 添加元素
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public MyArray Add(string str)
        {
            //要溢出的时候扩容
            if (count == array.Length)
            {
                string[] newArray = new string[2 * array.Length];
                array.CopyTo(newArray, 0);
                array = newArray;//array重新指向
            }
            array[count  ] = str;
            return this;
        }
    
        /// <summary>
        /// 移除某一项
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public MyArray RemoveAt(int i)
        {
            for (int j = i; j < count - 1; j  )
            {
                array[j] = array[j   1];
            }
            count--;//少了一个元素所以--
            return this;
        }
    
        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public string this[int index]
        {
            get
            {
                return array[index];
            }
            set
            {
                array[index] = value;
            }
        }
    }
    

    MyArrayExt.cs文件:

    public partial class MyArray: IEnumerable
    {
        /// <summary>
        /// 枚举器方法
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return new MyEnumerator(this.array, this.count);
        }
    }
    

    调用:

    static void Main(string[] args)
    {
        MyArray array = new MyArray();
        array.Add("~").Add("这").Add("是").Add("一").Add("个").Add("测").Add("试").Add("。").RemoveAt(0).RemoveAt(3).RemoveAt(6);
        for (int i = 0; i < array.Length; i  )
        {
            Console.Write(array[i]);
        }
        Console.WriteLine();
        foreach (var item in array)
        {
            Console.Write(item);
        }
    }
    

    结果:

    这是一测试
    这是一测试
    

     

    5、多态的理解

    a = list(range(10))

    7.2.yield方式¶

    小明看着班里女生羡慕的眼神,得意的强调道:

    注意一下,C#是用yield return xxx,Python是用yield xxx关键字

    还记得开头说的那句话吗?(yield官方文档)

    其实迭代器(iterator)就是为了更简单的创建枚举器(enumerator)和可枚举类型(enumerator type)的方式

    如果枚举器和可枚举类型还是不理解(举个例子)就懂了:(从遍历方式就看出区别了)

    定义一个斐波拉契函数,返回可枚举类型

    /// <summary>
    /// 返回一个可枚举类型
    /// </summary>
    public static IEnumerable<int> FibonaByIEnumerable(int n)
    {
        int a = 0;
        int b = 1;
        for (int i = 0; i < n; i  )
        {
            yield return b;
            (a, b) = (b, a   b);
        }
    }
    

    调用:

    foreach (var item in FibonaByIEnumerable(30))
    {
        Console.WriteLine(item);
    }
    

    定义一个斐波拉契函数,返回一个枚举器

    /// <summary>
    /// 返回一个枚举器
    /// </summary>
    public static IEnumerator<int> FibonaByIEnumerator(int n)
    {
        int a = 0;
        int b = 1;
        for (int i = 0; i < n; i  )
        {
            yield return b;
            (a, b) = (b, a   b);
        }
    }
    

    调用一下:

    var tmp = FibonaByIEnumerator(30);
    while (tmp.MoveNext())
    {
        Console.WriteLine(tmp.Current);
    }
    

    利用yield轻轻松松就创建了枚举器和可枚举类型

    以上面那个MyArray的案例来说,有了yield我们代码量大大简化:(Demo)

    MyArray.cs

    public partial class MyArray
    {
        /// <summary>
        /// 数组容量
        /// </summary>
        private string[] array = new string[4];
        /// <summary>
        /// 数组元素个数
        /// </summary>
        private int count = 0;
        /// <summary>
        /// 当前数组的长度
        /// </summary>
        public int Length
        {
            get
            {
                return count;
            }
        }
    
        /// <summary>
        /// 添加元素
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public MyArray Add(string str)
        {
            //要溢出的时候扩容
            if (count == array.Length)
            {
                string[] newArray = new string[2 * array.Length];
                array.CopyTo(newArray, 0);
                array = newArray;//array重新指向
            }
            array[count  ] = str;
            return this;
        }
    
        /// <summary>
        /// 移除某一项
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public MyArray RemoveAt(int i)
        {
            for (int j = i; j < count - 1; j  )
            {
                array[j] = array[j   1];
            }
            array[count - 1] = string.Empty;//add 干掉移除的数组
            count--;//少了一个元素所以--
            return this;
        }
    
        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public string this[int index]
        {
            get
            {
                return array[index];
            }
            set
            {
                array[index] = value;
            }
        }
    }
    

    MyArrayExt.cs

    public partial class MyArray : IEnumerable
    {
        /// <summary>
        /// 枚举器方法
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return MyEnumerator();
        }
        /// <summary>
        /// 通过yield快速实现
        /// </summary>
        /// <returns></returns>
        public IEnumerator<string> MyEnumerator()
        {
            foreach (var item in this.array)
            {
                yield return item;
            }
        }
    }
    

    然后就行了,MyEnumerator都不用你实现了:

    MyArray array = new MyArray();
    array.Add("~").Add("这").Add("是").Add("一").Add("个").Add("测").Add("试").Add("。").RemoveAt(0).RemoveAt(3).RemoveAt(6);
    for (int i = 0; i < array.Length; i  )
    {
        Console.Write(array[i]);
    }
    Console.WriteLine();
    foreach (var item in array)
    {
        Console.Write(item);
    }
    

    结果:

    这是一测试
    这是一测试
    

    扩充一下:Python退出迭代器用yield return 或者 yield breakC#使用yield break来退出迭代

    做个 demo 测试下:

    public static IEnumerable<int> GetValue()
    {
        for (int i = 0; i < 5; i  )
        {
            yield return i;
            if (i == 2)
            {
                yield break;
            }
        }
    }
    

    调用:

    static void Main(string[] args)
    {
        foreach (var item in GetValue())
        {
            Console.WriteLine(item);
        }
    }
    

    输出:

    0
    1
    2
    

     

    多态,一个对象在不同的情况下显示不同的形态。在python中因为是弱类型语言,对类型没有限定,所有python中不完全支持多态,但是多态的思想呢,python也是能体现的。

    b = list(a)

    8.闭包¶

    1.1.1私有属性添加getter和setter方法

    ·copy模块中的copy函数

    8.1.Python闭包¶

    又到了上课时间,小明灰溜溜的跑回座位,听老师讲起了闭包的知识:

    函数方面还有不懂的可以看之前讲的文档:Function Base

    函数除了可以接受函数作为参数外,还可以把函数作为结果值返回(有点类似于C 里面的函数指针了)

    来看一个可变参数求和的例子:

    In [1]:

    def slow_sum(*args):
        def get_sum():
            sum = 0
            for i in args:
                sum  = i
            return sum
    
        return get_sum  # 返回函数引用地址(不加括号)
    
    a = slow_sum(1, 2, 3, 4, 5)# 返回get_sum函数的引用
    print(a)# 看看引用地址
    print(a())# a() 这时候才是调用get_sum()函数
    

     

    <function slow_sum.<locals>.get_sum at 0x7f57783b6268>
    15
    

     

    其实上面一个案例就是闭包(Closure)了,来个定义:

    在函数内部再定义一个函数,并且这个函数用到了外边函数的变量(参数或者局部变量),那么将这个函数以及用到的一些变量称之为闭包

    通俗点说就是:内部函数使用了外部函数作用域里的变量了,那这个内部函数和它用到的变量就是个闭包

    注意:当我们调用slow_sum()时,每次调用都会返回一个新的函数(相同的参数也一样)

    In [2]:

    a = slow_sum(1, 2, 3, 4)
    b = slow_sum(1, 2, 3, 4)
    
    a is b
    
    # a()和b()的调用结果互不影响
    

    Out[2]:

    False
    

     

    由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,所以也容易消耗内存

    so ==> 除非你真正需要它,否则不要使用闭包

    返回函数尽量不要引用任何循环变量,或者后续会发生变化的变量(容易出错)

    看着小明一脸懵圈的样子,老师说道:

    新讲的知识点一般都不太容易快速消化,我们再来看个闭包的好处就理解了:

    比如现在我们要根据公式来求解,以y=ax b为例,传统方法解决:

    In [3]:

    # 定义一个y=ax b的函数公式
    def get_value(a, b, x):
        return a * x   b
    

    In [4]:

    # 每次调用都得传 a,b
    print(get_value(2, 1, 1))
    print(get_value(2, 1, 2))
    print(get_value(2, 1, 3))
    print(get_value(2, 1, 4))
    

     

    3
    5
    7
    9
    

     

    每次调用都得额外传a、b的值

    就算使用偏函数来简化也不合适(毕竟已经是一个新的函数了):

    In [5]:

    from functools import partial
    
    new_get_value = partial(get_value, 2, 1)
    
    print(new_get_value(1))
    print(new_get_value(2))
    print(new_get_value(3))
    print(new_get_value(4))
    print(new_get_value(5))
    

     

    3
    5
    7
    9
    11
    

     

    简单总结functools.partial的作用就是:

    把一个函数的某些参数设置默认值,返回一个新的函数,然后调用新函数就免得你再输入重复参数了

    而这时候使用闭包就比较合适了,而且真的是封装了一个通用公式了

    a,b的值你可以任意变来生成新的公式,而且公式之间还不干扰,以 y=ax² bx c为例:

    In [6]:

    def quadratic_func(a, b, c):
        """y=ax² bx c"""
    
        def get_value(x):
            return a * x * x   b * x   c
    
        return get_value
    

    In [7]:

    # 来个简单的:x^2 1
    f1 = quadratic_func(1, 0, 1)
    
    print(f1(0))
    print(f1(1))
    print(f1(2))
    print(f1(3))
    print(f1(4))
    print(f1(5))
    

     

    1
    2
    5
    10
    17
    26
    

    In [8]:

    # 可能不太形象,我们画个图看看:
    
    import matplotlib.pyplot as plt # 导入matplotlib的pyplot模块
    

    In [9]:

    # 生成x和y的值
    x_list = list(range(-10, 11))
    y_list = [x * x   1 for x in x_list]
    
    print(x_list)
    print(y_list)
    
    # 画图
    plt.plot(x_list, y_list)
    # 显示图片
    plt.show()
    

     

    [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    [101, 82, 65, 50, 37, 26, 17, 10, 5, 2, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 101]
    

     

    新葡亰496net 10

    In [10]:

    # 再来个简单的:x^2-1
    f2 = quadratic_func(1, 0, -1) # 相互之间不干扰
    
    print(f2(0))
    print(f2(1))
    print(f2(2))
    print(f2(3))
    print(f2(4))
    print(f2(5))
    

     

    -1
    0
    3
    8
    15
    24
    

     

    新葡亰496net 11

    importcopy

    8.2.CSharp闭包¶

    听完闭包老师就下课了,说什么明天接着闭包讲啥装饰器的。

    小明一愣一愣的,然后就屁颠的跑黑板前讲起了C#版本的闭包:

    先看看怎么定义一个闭包,和Python一样,用个求和函数举例:(返回一个匿名函数

    // 有返回值就用Func,没有就用Action
    public static Func<int> SlowSum(params int[] args)
    {
        return () =>
        {
            int sum = 0;
            foreach (var item in args)
            {
                sum  = item;
            }
            return sum;
        };
    }
    

    调用:

    static void Main(string[] args)
    {
        var f1 = SlowSum(1, 2, 3, 4, 5);
        Console.WriteLine(f1);
        Console.WriteLine(f1());
    }
    

    结果:(从结果可以看到,f1是一个函数,等你调用f1()才会求和)

    System.Func`1[System.Int32]
    15
    

    接着讲 ~ 以上面的 y=ax² bx c为例,C#实现:

    // 以上面的 y=ax² bx c 为例,C#实现:
    public static Func<double, double> QuadraticFunc(double a, double b, double c)
    {
        return x => a * x * x   b * x   c; // 返回一个匿名函数
    }
    

    调用:

    static void Main(string[] args)
    {
        var func = QuadraticFunc(1, 0, 1);
    
        Console.WriteLine(func(0));
        Console.WriteLine(func(1));
        Console.WriteLine(func(2));
        Console.WriteLine(func(3));
        Console.WriteLine(func(4));
        Console.WriteLine(func(5));
    }
    

    结果:

    1
    2
    5
    10
    17
    26
    

    Func<double,double>不理解就看看定义就懂了:public delegate TResult Func<in T, out TResult>(T arg);

    这部分不是很难,简单提一下知识点即可。如果你想深究可以==> (点 点 点)

    在收获满满一箩筐眼球后,小明拍拍屁股去了新开的饭店大吃一顿了...


    写在最后:还有一些内容没写,估计过几天又有一篇叫 “基础拓展” 的文章了,为啥不一起写完呢?

    其实逆天也想写完,真写完文章又被叫做长篇大论一百页了 #^_^# 行了,听取大家意见,不写那么长的文章,下次见~

    使用property升级getter和setter方法

    a = (1,2,3)

    新葡亰496net 12

    b = copy.copy(a)

    运行结果:

    1.3属性property

    新葡亰496net 13

    面试题:

    1.1.1使用property取代getter和setter方法

    1、你对面向对象的理解

    @property成为属性函数,可以对属性赋值时做必要的检查,并保证代码的清晰短小,主要有2个作用

    2、面向对象的特征是什么

    ·将方法转换为只读

    3、对封装的理解?

    重新实现一个属性的设置和读取方法,可做边界判定

    封装,类本身就是一个封装,封装了属性和方法。方法也是封装,对一些业务逻辑的封装。私有也是封装,将一些方法和属性私有化,对外提供可访问的接口。

    新葡亰496net 14

    4、对继承的理解

    新葡亰496net 15

    将共性的内容放在父类中,子类只需要关注自己特有的内容,共性的继承过来就行了。

    1.1生成器

    这样简化开发,符合逻辑习惯,利于扩展。

    1.1.1什么是生成器

    5、多态的理解

    通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

    多态,一个对象在不同的情况下显示不同的形态。在python中因为是弱类型语言,对类型没有限定,所有python中不完全支持多态,但是多态的思想呢,python也是能体现的。

    1.1.1创建生成器方法1

    1.3.1私有属性添加getter和setter方法

    要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的[ ]改成( )

    classMoney(object):

    新葡亰496net 16

    def__init__(self):

    新葡亰496net 17

    self.__money =0

    创建l和g的区别仅在于最外层的[ ]和( ),l是一个列表,而g是一个生成器。我们可以直接打印出l的每一个元素,但我们怎么打印出g的每一个元素呢?如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值:

    defgetMoney(self):

    新葡亰496net 18

    returnself.__money

    新葡亰496net 19

    defsetMoney(self, value):

    新葡亰496net 20

    ifisinstance(value, int):

    生成器保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的异常。当然,这种不断调用next()实在是太变态了,正确的方法是使用for循环,因为生成器也是可迭代对象。所以,我们创建了一个生成器后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration异常。

    self.__money = value

    1.1.1创建生成器方法2

    else:

    generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

    print("error:不是整型数字")

    比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

    1.3.2使用property升级getter和setter方法

    1, 1, 2, 3, 5, 8, 13, 21, 34, ...

    classMoney(object):

    斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

    def__init__(self):

    新葡亰496net 21

    self.__money =0

    仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

    defgetMoney(self):

    也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

    returnself.__money

    新葡亰496net 22

    defsetMoney(self, value):

    在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

    ifisinstance(value, int):

    新葡亰496net 23

    self.__money = value

    但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

    else:

    新葡亰496net 24

    print("error:不是整型数字")

    1.1.1send

    money = property(getMoney, setMoney)

    例子:执行到yield时,gen函数作用暂时保存,返回i的值;temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)

    运行结果:

    使用next函数

    In [1]:fromget_setimportMoney

    新葡亰496net 25

    In [2]:

    使用__next__()方法

    In [2]: a = Money()

    新葡亰496net 26

    In [3]:

    使用send

    In [3]: a.money

    新葡亰496net 27

    Out[3]:0

    新葡亰496net 28

    In [4]: a.money =100

    可以使用send传入的值进行判断

    In [5]: a.money

    新葡亰496net 29

    Out[5]:100

    1.1.1实现多任务

    In [6]: a.getMoney()

    模拟多任务实现方式之一:协程

    Out[6]:100

    新葡亰496net 30

    1.3.3使用property取代getter和setter方法

    新葡亰496net 31

    @property成为属性函数,可以对属性赋值时做必要的检查,并保证代码的清晰短小,主要有2个作用

    总结

    ·将方法转换为只读

    生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

    ·重新实现一个属性的设置和读取方法,可做边界判定

    生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

    classMoney(object):

    生成器的特点:

    def__init__(self):

    1.节约内存

    self.__money =0

    2.迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

    @property

    1.1迭代器

    defmoney(self):

    迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

    returnself.__money

    1.1.1可迭代对象

    @money.setter

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

    defmoney(self, value):

    一类是集合数据类型,如list、tuple、dict、set、str等;

    ifisinstance(value, int):

    一类是generator,包括生成器和带yield的generator function。

    self.__money = value

    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

    else:

    1.1.1判断是否可以迭代

    print("error:不是整型数字")

    可以使用isinstance()判断一个对象是否是Iterable对象:

    运行结果

    新葡亰496net 32

    In [3]: a = Money()

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

    In [4]:

    1.1.1迭代器

    In [4]:

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

    In [4]: a.money

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

    Out[4]:0

    新葡亰496net 33

    In [5]: a.money =100

    1.1.1iter()函数

    In [6]: a.money

    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

    Out[6]:100

    把list、dict、str等Iterable变成Iterator可以使用iter()函数:

    1.4生成器

    新葡亰496net 34

    1.4.1什么是生成器

    总结

    通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

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

    1.4.2创建生成器方法1

    ·凡是可作用于next()函数的对象都是Iterator类型

    要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的[ ]改成( )

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

    In [15]: L = [ x*2forxinrange(5)]

    ·目的是在使用集合的时候,减少占用的内容。

    In [16]: L

    1.1闭包

    Out[16]: [0,2,4,6,8]

    1.1.1函数引用

    In [17]: G = ( x*2forxinrange(5))

    新葡亰496net 35

    In [18]: G

    1.1.1什么是闭包

    Out[18]: at0x7f626c132db0>

    新葡亰496net 36

    In [19]:

    新葡亰496net 37

    创建L和G的区别仅在于最外层的[ ]和( ),L是一个列表,而G是一个生成器。我们可以直接打印出L的每一个元素,但我们怎么打印出G的每一个元素呢?如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值:

    1.1.1看一个闭包的实际例子:

    In [19]: next(G)

    新葡亰496net 38

    Out[19]: 0

    这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x 1和y = 4x 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

    In [20]: next(G)

    如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。

    Out[20]: 2

    闭包思考:

    In [21]: next(G)

    1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成

    Out[21]: 4

    2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

    In [22]: next(G)

    Out[22]: 6

    In [23]: next(G)

    Out[23]: 8

    In [24]: next(G)


    StopIteration                             Traceback (most recent call last)

    in ()

    ----> 1 next(G)

    StopIteration:

    In [25]:

    In [26]: G = ( x*2forxinrange(5))

    In [27]:forxinG:

    ....:     print(x)

    ....:

    0

    2

    4

    6

    8

    In [28]:

    生成器保存的是算法,每次调用next(G),就计算出G的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的异常。当然,这种不断调用next()实在是太变态了,正确的方法是使用for循环,因为生成器也是可迭代对象。所以,我们创建了一个生成器后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration异常。

    1.4.3创建生成器方法2

    generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

    比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

    1, 1, 2, 3, 5, 8, 13, 21, 34, ...

    斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

    In [28]:deffib(times):

    ....:     n =0

    ....:     a,b =0,1

    ....:whilen

    ....:         print(b)

    ....:         a,b = b,a b

    ....:         n =1

    ....:return'done'

    ....:

    In [29]: fib(5)

    1

    1

    2

    3

    5

    Out[29]:'done'

    仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

    也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

    In [30]: def fib(times):

    ....:     n = 0

    ....:     a,b = 0,1

    ....:     while n

    ....:         yield b

    ....:         a,b = b,a b

    ....:         n =1

    ....:     return 'done'

    ....:

    In [31]: F = fib(5)

    In [32]: next(F)

    Out[32]: 1

    In [33]: next(F)

    Out[33]: 1

    In [34]: next(F)

    Out[34]: 2

    In [35]: next(F)

    Out[35]: 3

    In [36]: next(F)

    Out[36]: 5

    In [37]: next(F)


    StopIteration                             Traceback (most recent call last)

    in ()

    ----> 1 next(F)

    StopIteration: done

    在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

    In [38]:forninfib(5):

    ....:     print(n)

    ....:

    1

    1

    2

    3

    5

    In [39]:

    但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

    In [39]: g = fib(5)

    In [40]:whileTrue:

    ....:try:

    ....:         x = next(g)

    ....:         print("value:%d"%x)

    ....:exceptStopIterationase:

    ....:         print("生成器返回值:%s"%e.value)

    ....:break

    ....:

    value:1

    value:1

    value:2

    value:3

    value:5

    生成器返回值:done

    In [41]:

    1.4.4send

    例子:执行到yield时,gen函数作用暂时保存,返回i的值;temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)

    In [10]:defgen():

    ....:     i =0

    ....:whilei<5:

    ....:         temp =yieldi

    ....:         print(temp)

    ....:         i =1

    ....:

    使用next函数

    In [11]: f = gen()

    In [12]: next(f)

    Out[12]: 0

    In [13]: next(f)

    None

    Out[13]: 1

    In [14]: next(f)

    None

    Out[14]: 2

    In [15]: next(f)

    None

    Out[15]: 3

    In [16]: next(f)

    None

    Out[16]: 4

    In [17]: next(f)

    None


    StopIteration                             Traceback (most recent call last)

    in ()

    ----> 1 next(f)

    StopIteration:

    使用__next__()方法

    In [18]: f = gen()

    In [19]: f.__next__()

    Out[19]: 0

    In [20]: f.__next__()

    None

    Out[20]: 1

    In [21]: f.__next__()

    None

    Out[21]: 2

    In [22]: f.__next__()

    None

    Out[22]: 3

    In [23]: f.__next__()

    None

    Out[23]: 4

    In [24]: f.__next__()

    None


    StopIteration                             Traceback (most recent call last)

    in ()

    ----> 1 f.__next__()

    StopIteration:

    使用send

    In [43]: f = gen()

    In [44]: f.__next__()

    Out[44]:0

    In [45]: f.send('haha')

    haha

    Out[45]:1

    In [46]: f.__next__()

    None

    Out[46]:2

    In [47]: f.send('haha')

    haha

    Out[47]:3

    In [48]:

    1.4.5实现多任务

    模拟多任务实现方式之一:协程

    def test1():

    while True:

    print("--1--")

    yield None

    def test2():

    while True:

    print("--2--")

    yield None

    t1 = test1()

    t2 = test2()

    while True:

    t1.__next__()

    t2.__next__()

    总结

    生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

    生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

    生成器的特点:

    1.节约内存

    2.迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

    1.5迭代器

    迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

    1.5.1可迭代对象

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

    一类是集合数据类型,如list、tuple、dict、set、str等;

    一类是generator,包括生成器和带yield的generator function。

    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

    1.5.2判断是否可以迭代

    可以使用isinstance()判断一个对象是否是Iterable对象:

    In [50]:fromcollectionsimportIterable

    In [51]: isinstance([], Iterable)

    Out[51]:True

    In [52]: isinstance({}, Iterable)

    Out[52]:True

    In [53]: isinstance('abc', Iterable)

    Out[53]:True

    In [54]: isinstance((xforxinrange(10)), Iterable)

    Out[54]:True

    In [55]: isinstance(100, Iterable)

    Out[55]:False

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

    1.5.3迭代器

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

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

    In [56]:fromcollectionsimportIterator

    In [57]: isinstance((xforxinrange(10)), Iterator)

    Out[57]:True

    In [58]: isinstance([], Iterator)

    Out[58]:False

    In [59]: isinstance({}, Iterator)

    Out[59]:False

    In [60]: isinstance('abc', Iterator)

    Out[60]:False

    In [61]: isinstance(100, Iterator)

    Out[61]:False

    1.5.4iter()函数

    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

    把list、dict、str等Iterable变成Iterator可以使用iter()函数:

    In [62]: isinstance(iter([]), Iterator)

    Out[62]:True

    In [63]: isinstance(iter('abc'), Iterator)

    Out[63]:True

    总结

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

    ·凡是可作用于next()函数的对象都是Iterator类型

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

    ·目的是在使用集合的时候,减少占用的内存。

    1.6闭包

    1.61.1.1什么是闭包.1函数引用**

    deftest1():

    print("--- in test1 func----")

    #调用函数

    test1()

    #引用函数

    ret = test1

    print(id(ret))

    print(id(test1))

    #通过引用调用函数

    ret()

    运行结果:

    ---intest1 func----

    140212571149040

    140212571149040

    ---intest1 func----

    1.6.2 什么是闭包

    定义一个函数

    deftest(number):

    #在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包

    deftest_in(number_in):

    print("in test_in函数, number_in is %d"%number_in)

    returnnumber number_in

    #其实这里返回的就是闭包的结果

    returntest_in

    #给test函数赋值,这个20就是给参数number

    ret = test(20)

    #注意这里的100其实给参数number_in

    print(ret(100))

    #注意这里的200其实给参数number_in

    print(ret(200))

    运行结果:

    intest_in函数, number_inis100

    120

    intest_in函数, number_inis200

    220

    1.6.3看一个闭包的实际例子:

    defline_conf(a, b):

    defline(x):

    returna*x b

    returnline

    line1 = line_conf(1,1)

    line2 = line_conf(4,5)

    print(line1(5))

    print(line2(5))

    这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x 1和y = 4x 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

    如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。

    闭包思考:

    1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成

    2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

    1.7.装饰器

    装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以这也是Python面试中必问的问题,但对于好多初次接触这个知识的人来讲,这个功能有点绕,自学时直接绕过去了,然后面试问到了就挂了,因为装饰器是程序开发的基础知识,这个都不会,别跟人家说你会Python,看了下面的文章,保证你学会装饰器。

    1.7.1装饰器的理解

    1、先明白这段代码

    ####第一波####

    deffoo():

    print('foo')

    foo#表示是函数

    foo()#表示执行foo函数

    ####第二波####

    deffoo():

    print('foo')

    foo =lambdax: x 1

    foo()#执行下面的lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数

    2、需求来了

    初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:

    ###############基础平台提供的功能如下###############

    deff1():

    print('f1')

    deff2():

    print('f2')

    deff3():

    print('f3')

    deff4():

    print('f4')

    ###############业务部门A调用基础平台提供的功能###############

    f1()

    f2()

    f3()

    f4()

    ###############业务部门B调用基础平台提供的功能###############

    f1()

    f2()

    f3()

    f4()

    目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

    老大把工作交给Low B,他是这么做的:

    跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了。太棒了,有充足的时间泡妹子...

    当天Low B被开除了…

    老大把工作交给Low BB,他是这么做的:

    ###############基础平台提供的功能如下###############

    deff1():

    #验证1

    #验证2

    #验证3

    print('f1')

    deff2():

    #验证1

    #验证2

    #验证3

    print('f2')

    deff3():

    #验证1

    #验证2

    #验证3

    print('f3')

    deff4():

    #验证1

    #验证2

    #验证3

    print('f4')

    ###############业务部门不变###############

    ###业务部门A调用基础平台提供的功能###

    f1()

    f2()

    f3()

    f4()

    ###业务部门B调用基础平台提供的功能###

    f1()

    f2()

    f3()

    f4()

    过了一周Low BB被开除了…

    老大把工作交给Low BBB,他是这么做的:

    只对基础平台的代码进行重构,其他业务部门无需做任何修改

    ###############基础平台提供的功能如下###############

    defcheck_login():

    #验证1

    #验证2

    #验证3

    pass

    deff1():

    check_login()

    print('f1')

    deff2():

    check_login()

    print('f2')

    deff3():

    check_login()

    print('f3')

    deff4():

    check_login()

    print('f4')

    老大看了下Low BBB的实现,嘴角漏出了一丝的欣慰的笑,语重心长的跟Low BBB聊了个天:

    老大说:

    写代码要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

    ·封闭:已实现的功能代码块

    新葡亰496net:基础衍生,高级编程。·开放:对扩展开发

    如果将开放封闭原则应用在上述需求中,那么就不允许在函数f1、f2、f3、f4的内部进行修改代码,老板就给了Low BBB一个实现方案:

    defw1(func):

    definner():

    #验证1

    #验证2

    #验证3

    func()

    returninner

    @w1

    deff1():

    print('f1')

    @w1

    deff2():

    print('f2')

    @w1

    deff3():

    print('f3')

    @w1

    deff4():

    print('f4')

    对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数f1 f2 f3 f4之前都进行【验证】操作,并且其他业务部门无需做任何操作。

    Low BBB心惊胆战的问了下,这段代码的内部执行原理是什么呢?

    老大正要生气,突然Low BBB的手机掉到地上,恰巧屏保就是Low BBB的女友照片,老大一看一紧一抖,喜笑颜开,决定和Low BBB交个好朋友。

    详细的开始讲解了:

    单独以f1为例:

    defw1(func):

    definner():

    #验证1

    #验证2

    #验证3

    func()

    returninner

    @w1

    deff1():

    print('f1')

    python解释器就会从上到下解释代码,步骤如下:

    1.def w1(func): ==>将w1函数加载到内存

    2.@w1

    没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。

    从表面上看解释器着实会执行这两句,但是@w1这一句代码里却有大文章,@函数名 是python的一种语法糖。

    上例@w1内部会执行一下操作:

    执行w1函数

    执行w1函数 ,并将@w1下面的函数作为w1函数的参数,即:@w1等价于w1(f1)所以,内部就会去执行:

    definner():

    #验证1

    #验证2

    #验证3

    f1()# func是参数,此时func等于f1

    returninner#返回的inner,inner代表的是函数,非执行函数,其实就是将原来的f1函数塞进另外一个函数中

    w1的返回值

    将执行完的w1函数返回值 赋值 给@w1下面的函数的函数名f1即将w1的返回值再重新赋值给f1,即:

    新f1 =definner():

    #验证1

    #验证2

    #验证3

    原来f1()

    returninner

    所以,以后业务部门想要执行f1函数时,就会执行 新f1函数,在新f1函数内部先执行验证,再执行原来的f1函数,然后将原来f1函数的返回值返回给了业务调用者。

    如此一来,即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用着

    Low BBB你明白了吗?要是没明白的话,我晚上去你家帮你解决吧!!!

    1.1.2多个装饰器

    #定义函数:完成包裹数据

    defmakeBold(fn):

    defwrapped():

    return"" fn() ""

    returnwrapped

    #定义函数:完成包裹数据

    defmakeItalic(fn):

    defwrapped():

    return"" fn() ""

    returnwrapped

    @makeBold

    deftest1():

    return"hello world-1"

    @makeItalic

    deftest2():

    return"hello world-2"

    @makeBold

    @makeItalic

    deftest3():

    return"hello world-3"

    print(test1()))

    print(test2()))

    print(test3()))

    运行结果:

    hello world-1

    hello world-2

    hello world-3

    1.7.3装饰器(decorator)功能

    1.引入日志

    2.函数执行时间统计

    3.执行函数前预备处理

    4.执行函数后清理功能

    5.权限校验等场景

    6.缓存

    1.7.4装饰器示例

    1.7.4.1例1:无参数的函数

    fromtimeimportctime, sleep

    deftimefun(func):

    defwrappedfunc():

    print("%s called at %s"%(func.__name__, ctime()))

    func()

    returnwrappedfunc

    @timefun

    deffoo():

    print("I am foo")

    foo()

    sleep(2)

    foo()

    上面代码理解装饰器执行行为可理解成

    foo = timefun(foo)

    #foo先作为参数赋值给func后,foo接收指向timefun返回的wrappedfunc

    foo()

    #调用foo(),即等价调用wrappedfunc()

    #内部函数wrappedfunc被引用,所以外部函数的func变量(自由变量)并没有释放

    #func里保存的是原foo函数对象

    1.7.2例2:被装饰的函数有参数

    fromtimeimportctime, sleep

    deftimefun(func):

    defwrappedfunc(a, b):

    print("%s called at %s"%(func.__name__, ctime()))

    print(a, b)

    func(a, b)

    returnwrappedfunc

    @timefun

    deffoo(a, b):

    print(a b)

    foo(3,5)

    sleep(2)

    foo(2,4)

    1.7.3例3:被装饰的函数有不定长参数

    fromtimeimportctime, sleep

    deftimefun(func):

    defwrappedfunc(*args, **kwargs):

    print("%s called at %s"%(func.__name__, ctime()))

    func(*args, **kwargs)

    returnwrappedfunc

    @timefun

    deffoo(a, b, c):

    print(a b c)

    foo(3,5,7)

    sleep(2)

    foo(2,4,9)

    1.7.4例4:装饰器中的return

    fromtimeimportctime, sleep

    deftimefun(func):

    defwrappedfunc():

    print("%s called at %s"%(func.__name__, ctime()))

    func()

    returnwrappedfunc

    @timefun

    deffoo():

    print("I am foo")

    @timefun

    defgetInfo():

    return'----hahah---'

    foo()

    sleep(2)

    foo()

    print(getInfo())

    执行结果:

    foo called at Fri Nov421:55:352016

    I am foo

    foo called at Fri Nov421:55:372016

    I am foo

    getInfo called at Fri Nov421:55:372016

    None

    如果修改装饰器为return func(),则运行结果:

    foo called at Fri Nov421:55:572016

    I am foo

    foo called at Fri Nov421:55:592016

    I am foo

    getInfo called at Fri Nov421:55:592016

    ----hahah---

    总结:

    ·一般情况下为了让装饰器更通用,可以有return

    1.7.5例5:装饰器带参数,在原有装饰器的基础上,设置外部变量

    #decorator2.py

    fromtimeimportctime, sleep

    deftimefun_arg(pre="hello"):

    deftimefun(func):

    defwrappedfunc():

    print("%s called at %s %s"%(func.__name__, ctime(), pre))

    returnfunc()

    returnwrappedfunc

    returntimefun

    @timefun_arg("wangcai")

    deffoo():

    print("I am foo")

    @timefun_arg("python")

    deftoo():

    print("I am too")

    foo()

    sleep(2)

    foo()

    too()

    sleep(2)

    too()

    可以理解为

    foo()==timefun_arg("wangcai")(foo)()

    1.2python是动态语言

    1.2.1动态语言的定义

    动态编程语言是高级程序设计语言的一个类别,在计算机科学领域已被广泛应用。它是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语言目前非常具有活力。例如JavaScript便是一个动态语言,除此之外如PHP、Ruby、Python等也都属于动态语言,而C、C 等语言则不属于动态语言。----来自 维基百科

    1.2.2运行的过程中给对象绑定(添加)属性

    >>>classPerson(object):

    def__init__(self, name = None, age = None):

    self.name = name

    self.age = age

    >>>P = Person("小明","24")

    >>>

    在这里,我们定义了1个类Person,在这个类里,定义了两个初始属性name和age,但是人还有性别啊!如果这个类不是你写的是不是你会尝试访问性别这个属性呢?

    >>>P.sex ="male"

    >>>P.sex

    'male'

    >>>

    这时候就发现问题了,我们定义的类里面没有sex这个属性啊!怎么回事呢? 这就是动态语言的魅力和坑! 这里 实际上就是 动态给实例绑定属性!

    1.2.3运行的过程中给类绑定(添加)属性

    >>>P1 = Person("小丽","25")

    >>>P1.sex

    Traceback (most recent call last):

    File"", line1,in

    P1.sex

    AttributeError: Person instance has no attribute'sex'

    >>>

    我们尝试打印P1.sex,发现报错,P1没有sex这个属性!----给P这个实例绑定属性对P1这个实例不起作用! 那我们要给所有的Person的实例加上sex属性怎么办呢? 答案就是直接给Person绑定属性!

    >>>> Person.sex =None#给类Person添加一个属性

    >>>P1 = Person("小丽","25")

    >>>print(P1.sex)#如果P1这个实例对象中没有sex属性的话,那么就会访问它的类属性

    None#可以看到没有出现异常

    >>>

    1.2.4运行的过程中给类绑定(添加)方法

    我们直接给Person绑定sex这个属性,重新实例化P1后,P1就有sex这个属性了! 那么function呢?怎么绑定?

    >>>classPerson(object):

    def__init__(self, name = None, age = None):

    self.name = name

    self.age = age

    defeat(self):

    print("eat food")

    >>>defrun(self, speed):

    print("%s在移动,速度是%d km/h"%(self.name, speed))

    >>>P = Person("老王",24)

    >>>P.eat()

    eat food

    >>>

    >>>P.run()

    Traceback (most recent call last):

    File"", line1,in

    P.run()

    AttributeError: Person instance has no attribute'run'

    >>>

    >>>

    >>>importtypes

    >>>P.run = types.MethodType(run, P)

    >>>P.run(180)

    老王在移动,速度是180km/h

    既然给类添加方法,是使用类名.方法名= xxxx,那么给对象添加一个方法也是类似的对象.方法名= xxxx

    完整的代码如下:

    importtypes

    #定义了一个类

    classPerson(object):

    num =0

    def__init__(self, name = None, age = None):

    self.name = name

    self.age = age

    defeat(self):

    print("eat food")

    #定义一个实例方法

    defrun(self, speed):

    print("%s在移动,速度是%d km/h"%(self.name, speed))

    #定义一个类方法

    @classmethod

    deftestClass(cls):

    cls.num =100

    #定义一个静态方法

    @staticmethod

    deftestStatic():

    print("---static method----")

    #创建一个实例对象

    P = Person("老王",24)

    #调用在class中的方法

    P.eat()

    #给这个对象添加实例方法

    P.run = types.MethodType(run, P)

    #调用实例方法

    P.run(180)

    #给Person类绑定类方法

    Person.testClass = testClass

    #调用类方法

    print(Person.num)

    Person.testClass()

    print(Person.num)

    #给Person类绑定静态方法

    Person.testStatic = testStatic

    #调用静态方法

    Person.testStatic()

    1.2.5运行的过程中删除属性、方法

    删除的方法:

    1.del对象.属性名

    2.delattr(对象, "属性名")

    通过以上例子可以得出一个结论:相对于动态语言,静态语言具有严谨性!所以,玩动态语言的时候,小心动态的坑!

    那么怎么避免这种情况呢?请使用__slots__,

    1.2.6__slots__

    现在我们终于明白了,动态语言与静态语言的不同

    动态语言:可以在运行的过程中,修改代码

    静态语言:编译时已经确定好代码,运行过程中不能修改

    如果我们想要限制实例的属性怎么办?比如,只允许对Person实例添加name和age属性。

    为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

    >>>classPerson(object):

    __slots__ = ("name","age")

    >>>P = Person()

    >>>P.name ="老王"

    >>>P.age =20

    >>>P.score =100

    Traceback (most recent call last):

    File"", line1,in

    AttributeError: Person instance has no attribute'score'

    >>>

    1.2.6.1注意:

    ·使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

    In [67]:classTest(Person):

    ...:pass

    ...:

    In [68]: t = Test()

    In [69]: t.score =100

    本文由新葡亰496net发布于电脑系统,转载请注明出处:新葡亰496net:基础衍生,高级编程

    关键词: