zoukankan      html  css  js  c++  java
  • 第一阶段:Python开发基础 day15 函数递归内置函数面向过程编程

    一、递归

    一、什么是函数递归?

    函数的嵌套调用是:函数嵌套函数。函数的递归调用:它是一种特殊的嵌套调用,但是它在调用一个函数的过程中,又直接或间接地调用了它自身。

    def foo():
        print('from foo')
        foo()
    
    foo()  # 进入死循环
    

    如果递归函数不断地调用函数自身,那么这个递归函数将会进入一个死循环,因此我们应该给递归函数一个明确的结束条件。

    1.1 直接调用

    直接调用指的是:直接在函数内部调用函数自身。

    import sys
    
    print(f"最大递归层数: {sys.getrecursionlimit()}")
    最大递归层数: 3000
    import sys
    
    # 修改递归层数
    sys.setrecursionlimit(10000)
    def foo(n):
        print('from foo',n)
        foo(n+1)
    foo(0)
    

    1.2 间接调用

    间接调用指的是:不在原函数体内调用函数自身,而是通过其他的方法间接调用函数自身。

    def bar():
        print('from bar')
        foo()
        
    def foo():
        print('from foo')
        bar()
        
    bar()
    

    递归必须要有两个明确的阶段:

    1. 递推:一层一层递归调用下去,进入下一层递归的问题规模都将会减小
    2. 回溯:递归必须要有一个明确的结束条件,在满足该条件开始一层一层回溯。

    递归的精髓在于通过不断地重复逼近一个最终的结果。

    53递归-代码.gif

    '''
    ...
    age(5) = age(4) + 2
    age(4) = age(3) + 2
    age(3) = age(2) + 2
    age(2) = age(1) + 2
    age(1) = 26
    
    
    age(n) = age(n-1) +2 
    age(1) = 26  # n=1
    '''
    
    def age(n):
        if n == 1:
            return 26
        res = age(n-1) + 2
        return res
    
    
    print(f"age(5): {age(5)}")
    

    age(5): 34

    二、为什么要用递归

    递归的本质就是干重复的活,但是仅仅是普通的重复,我们使用while循环就可以了。」

    lis = [1, [2, [3, [4, [5, [6, ]]]]]]
    
    
    def tell(lis):
        for i in lis:
            if type(i) is list:
                tell(i)
            else:
                print(i)
    
    
    # print(f"tell(lis): {tell(lis)}")
    tell(lis)
    
    

    1
    2
    3
    4
    5
    6

    三、如何用递归?

    3.1 二分法的应用

    有一个从小到大排列的整型数字列表,我们判断某一个数字是不是在这个列表里面。

    动图二分法查找数字23:

    53递归-二分23.gif

    动图二分法查找数字1:

    53递归-二分1.gif

    nums = [1, 3, 7, 11, 22, 34, 55, 78, 111, 115]
    
    for item in nums:
        if item == 10:
            print('find it')
            break
    else:
        print('not exists')
    not exists
    

    对于上述的列表我们可能可以通过一个for循环实现我们需要的功能,但是当我们的列表中的元素个数非常多时,我们还用这种方法,那是极其复杂的,因此我们可以考虑使用二分法的思想实现。

    from random import randint
    nums = [randint(1, 100) for i in range(100)]
    nums = sorted(nums)
    print(nums)
    
    [1, 2, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 10, 11, 11, 11, 11, 12, 13, 13, 15, 16, 16, 20, 21, 21, 23, 24, 26, 26, 27, 28, 28, 31, 33, 33, 34, 35, 38, 38, 39, 40, 42, 43, 45, 45, 46, 46, 47, 47, 51, 52, 52, 53, 53, 55, 55, 56, 56, 57, 57, 57, 58, 59, 61, 62, 64, 66, 66, 67, 68, 69, 69, 71, 72, 72, 74, 74, 75, 76, 78, 78, 79, 79, 79, 79, 80, 82, 85, 88, 89, 90, 90, 91, 91, 91, 94, 99, 99, 100]
    
    def search(search_num, nums):
        mid_index = len(nums)//2
        print(nums)
        if not nums:
            print('not exists')
            return
        if search_num > nums[mid_index]:
            # in the right
            nums = nums[mid_index+1:]
            search(search_num, nums)
        elif search_num < nums[mid_index]:
            # in the left
            nums = nums[:mid_index]
            search(search_num, nums)
        else:
            print('find it')
    
    
    search(7, nums)
    
    
    [1, 2, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 10, 11, 11, 11, 11, 12, 13, 13, 15, 16, 16, 20, 21, 21, 23, 24, 26, 26, 27, 28, 28, 31, 33, 33, 34, 35, 38, 38, 39, 40, 42, 43, 45, 45, 46, 46, 47, 47, 51, 52, 52, 53, 53, 55, 55, 56, 56, 57, 57, 57, 58, 59, 61, 62, 64, 66, 66, 67, 68, 69, 69, 71, 72, 72, 74, 74, 75, 76, 78, 78, 79, 79, 79, 79, 80, 82, 85, 88, 89, 90, 90, 91, 91, 91, 94, 99, 99, 100]
    [1, 2, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 10, 11, 11, 11, 11, 12, 13, 13, 15, 16, 16, 20, 21, 21, 23, 24, 26, 26, 27, 28, 28, 31, 33, 33, 34, 35, 38, 38, 39, 40, 42, 43, 45, 45, 46, 46, 47, 47]
    [1, 2, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 10, 11, 11, 11, 11, 12, 13, 13, 15, 16, 16, 20, 21]
    [1, 2, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7]
    [6, 6, 7, 7, 7]
    find it
    

    二、内置函数

    更多内置函数:https://docs.python.org/3/library/functions.html?highlight=built#ascii

    内置函数

    1.1 掌握

    1.bytes()

    解码字符。

    res = '你好'.encode('utf8')
    print(res)
    b'xe4xbdxa0xe5xa5xbd'
    res = bytes('你好', encoding='utf8')
    print(res)
    b'xe4xbdxa0xe5xa5xbd'
    

    2.chr()/ord()

    chr()参考ASCII码表将数字转成对应字符;ord()将字符转换成对应的数字。

    print(chr(65))
    A
    print(ord('A'))
    65
    

    3.divmod()

    分栏。

    print(divmod(10, 3))
    (3, 1)
    

    4.enumerate()

    带有索引的迭代。

    l = ['a', 'b', 'c']
    for i in enumerate(l):
        print(i)
    (0, 'a')
    (1, 'b')
    (2, 'c')
    

    5.eval()

    把字符串翻译成数据类型。

    lis = '[1,2,3]'
    lis_eval = eval(lis)
    print(lis_eval)
    [1, 2, 3]
    

    6.hash()

    是否可哈希。

    print(hash(1))
    1
    

    1.2 了解

    1.abs()

    求绝对值。

    print(abs(-13))  # 求绝对值
    13
    

    2.all()

    可迭代对象内元素全为真,则返回真。

    print(all([1, 2, 3, 0]))
    print(all([]))
    False
    True
    

    3.any()

    可迭代对象中有一元素为真,则为真。

    print(any([1, 2, 3, 0]))
    print(any([]))
    True
    False
    

    4.bin()/oct()/hex()

    二进制、八进制、十六进制转换。

    print(bin(17))
    print(oct(17))
    print(hex(17))
    0b10001
    0o21
    0x11
    

    5.dir()

    列举出所有time的功能。

    import time
    print(dir(time))
    ['_STRUCT_TM_ITEMS', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'altzone', 'asctime', 'clock', 'ctime', 'daylight', 'get_clock_info', 'gmtime', 'localtime', 'mktime', 'monotonic', 'perf_counter', 'process_time', 'sleep', 'strftime', 'strptime', 'struct_time', 'time', 'timezone', 'tzname', 'tzset']
    

    6.frozenset()

    不可变集合。

    s = frozenset({1, 2, 3})
    print(s)
    frozenset({1, 2, 3})
    

    7.globals()/loacals()

    查看全局名字;查看局部名字。

    # print(globals())
    def func():
        a = 1
    #     print(globals())
        print(locals())
    
    
    func()
    {'a': 1}
    

    8.pow()

    print(pow(3, 2, 3))  # (3**2)%3
    0
    

    9.round()

    print(round(3.5))
    4
    

    10.slice()

    lis = ['a', 'b', 'c']
    s = slice(1, 4, 1)
    print(lis[s])  # print(lis[1:4:1])
    ['b', 'c']
    

    11.sum()

    print(sum(range(100)))
    4950
    

    12.import()

    通过字符串导入模块。

    m = __import__('time')
    print(m.time())
    1556607502.334777
    

    1.3 面向对象知识点

    1. classmethod
    2. staticmethod
    3. property
    4. delattr
    5. hasattr
    6. getattr
    7. setattr
    8. isinstance()
    9. issubclass()
    10. object()
    11. super()

    三、面向过程编程

    面向过程编程是解决问题的一种思想,相当于武林门派,武林门派之间没有好坏之分,因此它与我们之后学习的面向对象编程其实没有好坏之分。

    面向过程编程,核心是编程二字,过程指的是解决问题的步骤,即先干什么、后干什么、再干什么、然后干什么……

    基于该思想编写程序就好比在设计一条流水线,面向对称编程其实是一种机械式的思维方式。

    当我们写登录功能,我们首先需要输入账号、密码,然后认证两次密码是否相同,然后从数据库中读取密码验证用户密码输入是否正确,然后输入验证码……之后,我们就能够实现登录功能。这样把登录功能问题流程化,进而是解决问题的思路非常清晰。

    56面向过程编程-流水线.jpg?x-oss-process=style/watermark

    优点:复杂的问题流程化,进而简单化。

    生产汽水瓶的流水线,没办法生产特斯拉。流水线下一个阶段的输入与上一个阶段的输出是有关联的。因此他的扩展性极差。

    缺点:扩展性差。

    一、注册功能

    1.1 接受用户输入用户名,进行合法性校验,拿到合法的用户名

    def check_username():
        username = input('username>>>').strip()
        if username.isalpha():
            return username
        else:
            print('用户名必须为字母,傻叉')
    

    1.2 接受用户输入密码,进行合法性校验,拿到合法的密码

    def check_pwd():
        while True:
            pwd = input('password>>>').strip()
            if len(pwd) < 5:
                print('密码长度至少五位')
                continue
            re_pwd = input('re_password>>>').strip()
            if pwd == re_pwd:
                return pwd
            else:
                print('两次输入密码不一致')
    

    1.3 将合法的用户名和密码写入文件

    def insert(username, pwd, path='57.txt'):
        with open(path, 'a', encoding='utf8') as fa:
            fa.write(f'{username}:{pwd}
    ')
    

    1.4 注册

    def register():
        username = check_username()
        pwd = check_pwd()
        insert(username, pwd)
        print(f'{username}注册成功')
    
    register()
    

    username>>>nick
    password>>>12345
    re_password>>>12345
    nick注册成功

    如果现在我们需要校验用户的年龄,因此我们需要增加一个check_age()方法,并且其他有牵连的地方都需要修改,因此它的扩展性极差。

    1.5 封装文件读写功能

    # def register():
    #     while True:
    #         username = input('username>>>').strip()
    #         # 检测用户是否重复,如果重复则重新输入
    #         with open('db.txt', 'r', encoding='utf8') as fr:
    #             for line in fr:
    #                 info = line.strip('
    ').split(':')
    #                 if username == info[0]:
    #                     print('用户名已经存在')
    #                     break
    #             else:
    #                 # 用户名不存在
    #                 # 跳出循环,不用重复输入用户名字
    #         res =
    
    # def tell_info():
    #     username = input('username>>>').strip()
    #     with open('db.txt', 'r', encoding='utf8') as fr:
    #         for line in fr:
    #             info = line.strip('
    ').split(':')
    #             if username == info[0]:
    #                 return info
    

    数据处理层

    def select(username):
        with open('db.txt', 'r', encoding='utf8') as fr:
            for line in fr:
                info = line.strip('
    ').split(':')
                if username == info[0]:
                    return info
    
    
    def tell_info():
        username = input('username>>>').strip()
        info = select(username)
        print(info)
    

    用户功能层

    def register():
        while True:
            username = input('username>>>').strip()
            # 检测用户是否重复,如果重复则重新输入
            res = select(username)
            if res:
                print('用户名已经存在')
            else:
                break
    
        while True:
            pwd = input('password>>>').strip()
            re_pwd = input('re_password>>>').strip()
            if pwd != re_pwd:
                print('两次输入密码不一致,请重新输入')
            else:
                break
    

    把注册功能分开之后,功能与功能直接解耦合,复杂的问题流程化,更加清晰。

    二、分层实现功能

    • 用户功能层:实现用户具体的功能。
    • 接口层:连接数据处理层和用户功能层。
    • 数据处理层:处理数据后把结果交给接口层。

    分层实现功能的好处:当我们需要实现web端和app端的软件,我们只要把数据处理层和接口层写好,然后实现不同的用户功能层即可,web端使用web端的用户功能层,app端使用app端的用户功能层,但是接口层和数据处理层是通用的。

    今日总结

    暂无

  • 相关阅读:
    ElasticSearch Java API 增删改查操作
    ElasticSearch Java API连接报错NoNodeAvailableException解决方案
    Elasticsearch 使用Kibana实现基本的增删改查+mget批量查询+bulk批量操作
    Elasticsearch CentOS6.5下安装ElasticSearch6.2.4+elasticsearch-head+Kibana
    Elasticsearch 核心概念:NRT+索引+分片+副本
    Solr solrj4与solrj-7.7.2操作solrcloud
    Solr Centos6.5下搭建solr-7.7.2集群solrcloud+DIH操作
    分层图【p2939】[USACO09FEB]改造路Revamping Trails
    NOIP2018提高组模拟题(二)
    树链剖分【p3178】[HAOI2015]树上操作
  • 原文地址:https://www.cnblogs.com/foreversun92/p/11358239.html
Copyright © 2011-2022 走看看