Python笔试面试要点详解(自用版)

1 解释型和编译型语言的区别

解释是翻译一句执行一句,更灵活,eg:python; 解释成机器能理解的指令,而不是二进制码
编译是整个源程序编译成机器可以直接执行的二进制可运行的程序,再运行这个程序 比如c++

2 简述下 Python 中的字符串、列表、元组和字典和集合

在 Python 中,字符串、列表、元组、字典和集合是常用的数据结构,每种结构都有其独特的特性和用途。

  1. 字符串 (String): ’ ’ " "

  2. 字符串是由字符组成的序列。在 Python 中,字符串是不可变的,这意味着一旦创建了一个字符串,就不能修改它。
  3. 字符串可以用单引号 (' ') 或双引号 (" ") 包围。
  4. 示例: 'hello'"world"
  5. 列表 (List): [ ]

  6. 列表是一个有序的元素集合,元素可以是任意类型(整数、浮点数、字符串、另一个列表等)。
  7. 列表是可变的,可以添加、删除或修改其中的元素。
  8. 列表使用方括号 ([ ]) 创建。
  9. 示例: [1, 2, 3, 'apple', [4, 5]]
  10. 元组 (Tuple): ()

  11. 元组与列表类似,也是一个有序的元素集合,但元组是不可变的。
  12. 元组使用圆括号 (( )) 创建,但空元组可以用单独的一个逗号创建,如 t = ()t = ,
  13. 由于元组不可变,它们在某些情况下比列表更高效。
  14. 示例: (1, 2, 3, 'banana')
  15. 字典 (Dictionary): {}

  16. 字典是一个无序的键值对集合。每个键在字典中都是唯一的,并与一个值相关联。
  17. 字典使用大括号 ({ }) 创建,键和值之间用冒号 (:) 分隔。
  18. 字典是可变的,可以增加、删除或修改键值对。
  19. 示例: {'name': 'Alice', 'age': 30}
  20. 集合 (Set): {}

  21. 集合是一个无序的、不重复的元素集合。
  22. 集合使用大括号 ({ }) 创建,但注意,空集合必须使用 set() 函数创建,而不是 {}(这会创建一个空字典)。
  23. 集合支持数学运算,如并集、交集、差集等。
  24. 示例: {1, 2, 3, 4, 5}

有序:元组 列表
唯一的:集合、字典
不可变:元组

3 简述上述数据类型的常用方法

  • 字符串:
    切片:[1:3]
    格式化:print(“Hello, {}! Today is {}.”.format(“Alice”, “Monday”))
    连接 a.join(b)
    替换 ‘ddd’.repalce(‘d’,‘a’)
    分割 .split(',') #用逗号分割
  • 列表
  • 切片
    添加元素 .append() .extend()
    删除

    del list[0] #下标
    list. pop() # 最后一个元素
    list.remove('c') # 元素

    排序 sort
    反转 reverse

  • 字典
    清空 .clear()
    遍历 key for key in dict
    新建 .formkeys(keyname, valuves)
  • 4 简述 Python 中的字符串编码

    UTF-8 是隶属于 Unicode 的可变长的编码方式。

    在 Python 中,以 Unicode 方式编码的字符串,可以使用 encode() 方法来编码成指定的 bytes,也可以通过 decode()方法来把 bytes编码成字符串。

    >>> "你好".encode('utf-8')
    b'\xe4\xbd\xa0\xe5\xa5\xbd'
    >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
    "你好"
    

    5 一行代码实现数值交换

    a, b = b, a

    6 is 和 == 的区别

    is是判断对象的id内存地址是否一致,==判断值
    可以理解为 是 和 等于 的区别

    7 Python 函数中的参数类型

    Python 函数的参数类型可以分为几种,包括:

    1. 位置参数(Positional Arguments)

    2. 这些参数必须按顺序提供,并且不能使用参数名来指定。
    3. 默认参数(Default Arguments)

    4. 函数定义时,可以为参数指定默认值。如果没有为这些参数提供值,则使用默认值。
    5. 关键字参数(Keyword Arguments)

    6. 这些参数在调用函数时使用参数名指定,这允许以任何顺序传递参数。
    7. 可变位置参数(Variable Positional Arguments):变的是数量

    8. 使用*表示,可以接收任意数量的位置参数,这些参数被存储在元组中。
    9. 可变关键字参数(Variable Keyword Arguments)

    10. 使用**表示,可以接收任意数量的关键字参数,这些参数被存储在字典中。
    11. 类型注解(Type Hints)

    12. Python 3.5+ 引入了类型注解,允许开发者指定参数预期的数据类型,但这不会对类型进行检查,只是作为提示。

    下面是一些示例来说明这些参数类型:

    
    # 类型注解
    def calculate(a: int, b: int, operator: str) -> int:
        if operator == '+':
            return a + b
        elif operator == '-':
            return a - b
    
    result = calculate(5, 3, '+')
    

    在实际编程中,使用默认参数和可变参数可以增加函数的灵活性。关键字参数和类型注解则可以提高代码的可读性和可维护性。类型注解在现代Python开发中越来越受欢迎,尤其是在大型项目和团队协作中,它们帮助工具和IDE进行类型检查,从而减少错误。

    8 *arg 和 **kwarg 作用

    可变位置和可变关键字,只数量可变。*arg 会把位置参数转化为 tuple,**kwarg 会把关键字参数转化为 dict。

    >>> def test(*arg, **kwarg):
    ...     if arg:
    ...         print("arg:", arg)
    ...     if kwarg:
    ...         print("kearg:", kwarg)
    ...
    >>> test('ni', 'hao', key='world')
    arg: ('ni', 'hao')
    kearg: {'key': 'world'}
    
    

    9 获取当前时间

    >>> import time
    >>> import datetime
    >>> print(datetime.datetime.now())
    2022-09-12 19:51:24.314335
    >>> print(time.strftime('%Y-%m-%d %H:%M:%S'))
    2022-09-12 19:51:24
    
    

    10 PEP8 规范

    PEP 8 是 Python 的官方编码风格指南,它提供了关于如何编写清晰、可读性强的 Python 代码的指导。以下是 PEP 8 中一些关键的规范:

    1. 缩进:4

    2. 使用 4 个空格进行缩进。
    3. 行宽

    4. 每行代码尽量不超过 79 个字符,这样可以在不同的设备上查看代码时不需要水平滚动。
    5. 空行:2

    6. 在函数定义之间使用两个空行。
    7. 在类定义之间也使用两个空行,但在类的方法定义之间使用一个空行。
    8. 导入:1

    9. 每个导入应该独占一行。
    10. 应该将 import 分为三部分:标准库导入、相关第三方导入、本地应用/库特定导入。
    11. 变量命名:小下划,大是类

    12. 使用小写字母和下划线分隔的方式命名变量和函数(snake_case)。
    13. 使用首字母大写的方式命名类(CamelCase)。
    14. 表达式和语句

    15. 不要在一行内写过多的语句。
    16. 避免使用复杂的表达式,尽量保持简单。
    17. 编码

    18. 代码应该默认使用 UTF-8 编码。
    19. 错误和异常

    20. 使用 raise 来引发异常,而不是 raise Exception()
    21. 代码注释

    22. 对复杂的操作进行注释,解释为什么代码要这么做。
    23. 类型注解

    24. 从 Python 3.5 开始,可以使用类型注解来提高代码的可读性和健壮性。
    25. 函数和方法

    26. 函数参数应该以单个空格分隔。
    27. 函数应该完成一个单一的任务。
    28. 命名空间

    29. 避免使用全局变量。
    30. 布尔上下文

    31. 在布尔上下文中,不要使用 if x: 这样的语句,除非 x 明显是一个布尔值。
    32. 序列

    33. 使用列表推导式来创建列表。[expression for item in iterable if condition],其中iterable:一个可迭代对象,比如列表、元组、字典等。
    34. even_numbers = [x for x in range(20) if x % 2 == 0] # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    35. squares_dict = {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
    36. 元组

    37. 单元素元组应该包含一个尾随逗号,例如 (a,)
    38. 字典

    39. 键值对应该使用等号 = 分隔,多个键值对之间用逗号分隔。
    40. 比较

    41. 使用 == 来检查对象的相等性,使用 is 来检查对象的同一性。
    42. 字符串

    43. 使用一致的方式表示字符串,通常是单引号 ' 或双引号 "

    PEP 8 只是一个指南,并不是强制的规则。但在 Python 社区中,遵循 PEP 8 可以使得代码风格统一,提高代码的可读性。此外,可以使用工具如 flake8pylint 来检查代码是否符合 PEP 8 的规范。

    Python 的深浅拷贝(🧡🧡,被问到 了,一问一个不吱声大哭)

    先了解一下引用,通过一个名字来用一个对象,举例:
    a = [1,2],此时a就是列表[1,2]这个对象的引用。如果b = a,b是列表[1,2]这个对象的另一个引用,当我改变a的值,b也会变。 所以,变量(名)是对象的引用,b这个新变量并没有创建新的实例,而是指向同一个对象的新引用。
    了解了引用,再来看浅拷贝。
    浅拷贝:copy.copy 拷贝前后的列表只有外层独立,内里嵌套列表还是共享的,你变他也变。对于内层只复制了元素的引用,没有复制元素本身,两个都指向同一个对象。而外层对象是一个新的对象,这个新的列表顶层对象包含了对子列表对象的引用,比如下面的:[1, 2, 3, [1, 2]]是新对象[1,2,3,[子列表对象的引用即1,2]]

    >>> import copy
    >>> list1 = [1, 2, 3, [1, 2]]
    >>> list2 = copy.copy(list1)
    >>> list2.append('a')
    >>> list2[3].append('a')
    >>> list1
    [1, 2, 3, [1, 2, 'a']]
    >>> list2
    [1, 2, 3, [1, 2, 'a'], 'a']
    
    

    深拷贝:copy.deepcopy 拷贝前后完全独立,深深断联,互不相干。因为复制的是元素本身,生成的是新对象。

    >>> import copy
    >>> list1 = [1, 2, 3, [1, 2]]
    >>> list3 = copy.deepcopy(list1)
    >>> list3.append('a')
    >>> list3[3].append('a')
    >>> list1
    [1, 2, 3, [1, 2]]
    >>> list3
    [1, 2, 3, [1, 2, 'a'], 'a']
    
    

    深拷贝比浅拷贝更消耗资源,因为它需要创建更多的对象。

    12 [lambda x:i*x for i in range(4)] 🧡🧡

    首先,lambda 匿名函数,为啥用?
    只有一行的函数,省去定义过程;不需要重复使用的,用完立即释放。
    其次,闭包。定义是:在内部函数里,引用了外部函数局部作用域的变量,使外层函数已经执行完毕。闭包可以访问创建时的环境状态。闭包函数是指当前函数用到上一层函数的局部作用域的变量时,触发了闭包规则的函数。注意只有在内部函数被调用时才去引用外部变量,没被调用时变量命名空间还不存在哩。
    回看[lambda x:i*x for i in range(4)],i是外层作用域,lambda x:ix是内层函数,在构建列表推导式时,对于每一个i都创建一个ix,就是[ix,ix,ix,ix]此时i = 3,所以内部的i*x还没开始计算,也就是lambda x内部还没被调用,i的递归只是创建了4个lambda 表达式。每个 lambda 表达式中的i只是对外部变量的引用

    >>> def num():
    ...     return [lambda x:i*x for i in range(4)] #返回的是一个函数列表[3x,3x,3x,3x]
    ...
    >>> [m(1) for m in num()] #对于函数列表中的每一个函数,赋值参数为1
    [3, 3, 3, 3]
    
    

    想要解决,确保了每个 lambda 表达式捕获了 i 的一个独立副本,变局部作用域为闭包作用域,局部作用是执行完毕就会被销毁,闭包作用域是外部已经执行完毕了,内部还可以访问外部的变量的值。

    >>> def num():
    ...     return [lambda x, i = i : i*x for i in range(4)]
    

    13 打印九九乘法表

    >>> for i in range(1, 10):
    ...     for j in range(1, i + 1):
    ...         print(f"{i}*{j}={i * j}", end=" ")
    ...     print()
    ...
    1*1=1
    2*1=2 2*2=4
    3*1=3 3*2=6 3*3=9
    4*1=4 4*2=8 4*3=12 4*4=16
    5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
    6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
    7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
    8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
    9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
    
    

    print函数默认是会换行的,其有一个默认参数 end;
    range左闭右开,打印一个下三角。

    14 filter、map、reduce 的作用

    reduce 递归遍历了每个元素,返回一个值,用一行代码解决求和、最大值、

    15. 为什么不建议函数的默认参数传入可变对象

    默认参数是可变的,那调用第二次传入的参数就是第一次调用完的结果,效果会叠加如下

    def append_to_element(element, append_to=[]):
       element.append(append_to)
       return element
    
    # 第一次调用函数,预期的行为
    print(append_to_element("new item"))  # 输出: ['new item']
    
    # 第二次调用函数,非预期的行为
    print(append_to_element("another item"))  # 输出: ['new item', 'another item']
    
    

    为了让每次调用都相互独立,不要用可变变量为默认参数,默认改为none,当是默认none时再创建一个[ ]

    def append_to_element(element, append_to=None):
        if append_to is None:
            append_to = []
        element.append(append_to)
        return element
    

    16. 面向对象中__new__ 和 init 区别(🧡🧡)

    new是类方法,用于创建一个实例对象,对其进行内存分配。参数是cls(类名)
    创建完实例对象之后,init作为实例方法,对实例对象进行初始化一些属性和参数。参数是self(实例名)
    在面向对象编程中,__new____init__ 是两个特殊的方法,它们在创建对象时有不同的用途和行为:

    举例说明:

    假设我们有一个简单的类 MyClass

    class MyClass:
        def __new__(cls, *args, **kwargs):
            # 一般new不需要重写,可以在这里执行一些逻辑,例如改变创建的对象类型
            print("Creating object with __new__")
            instance = super().__new__(cls)  # 创建对象,保留父类的方法,避免重复
            return instance
    
        def __init__(self, value):
            # 在这里设置对象的初始状态
            print("Initializing object with __init__")
            self.value = value
    
    # 创建 MyClass 的实例
    obj = MyClass(10)
    

    输出将是:

    Creating object with __new__
    Initializing object with __init__
    

    在这个例子中,__new__ 首先被调用并创建了 MyClass 的一个新实例。然后,__init__ 被调用,设置 value 属性。

    重写 __new__ 的例子:

    class BaseClass:
        def __new__(cls, *args, **kwargs):
            print("BaseClass __new__ called")
            return super().__new__(cls)
    
        def __init__(self, value):
            self.value = value
    
    class SubClass(BaseClass):
        def __new__(cls, *args, **kwargs):
            print("SubClass __new__ called, changing object type")
            instance = super().__new__(BaseClass)  # 创建 BaseClass 的实例
            return instance
    
        def __init__(self, value):
            super().__init__(value)  # 初始化 BaseClass 的 __init__
            print(f"SubClass initialized with value: {self.value}")
    
    # 创建 SubClass 的实例
    sub_obj = SubClass(20)
    

    输出将是:

    SubClass __new__ called, changing object type
    BaseClass __new__ called
    SubClass initialized with value: 20
    

    在这个例子中,我们重写了 SubClass__new__ 方法,使其创建了 BaseClass 的实例而不是 SubClass 的实例。这演示了 __new__ 方法如何控制对象的创建过程。

    通常,__new__ 方法在大多数情况下不需要重写,除非你有特殊的对象创建逻辑。而 __init__ 方法则更常用于初始化对象的状态。

    17. 三元运算规则

    执行1 if 条件 else 执行2

    >>> >>> a, b = 1, 2
    >>> h = a - b if a > b else a + b
    >>> h
    3
    
    

    18. 生成随机数

    用np.random也可以!

    >>> import random
    >>> random.random()
    0.7571910055209727
    >>> random.randint(1, 100)
    23
    >>> random.uniform(1, 5)  #random.uniform(a, b) 返回一个随机浮点数 N,使得 a <= N < b
    3.0640732831151687
    

    19. zip 函数用法

    zip() 函数将可迭代的对象作为参数,将对象中对应的元素打包成一个元组,然后返回由这些元组组成的列表。
    一对一打包成元组

    >>> list1 = ['zhangfei', 'guanyu', 'liubei', 'zhaoyun']
    >>> list2 = [0, 3, 2, 4]
    >>> list(zip(list1, list2))
    [('zhangfei', 0), ('guanyu', 3), ('liubei', 2), ('zhaoyun', 4)]
    

    20. range和xrange的区别

    只有Python2中才有xrange()和range(),Python3中的range()其实就是Python2中xrange()。即都用range

    21. with方法打开文件的作用

    打开文件在进行读写的时候可能会出现一些异常状况,如果按照常规的f.open()写法,我们需要 try,except,finally,做异常判断,并且文件最终不管遇到什么情况,都要执行f.close() 关闭文件,with方法帮我们实现了finally 中 f.close()。
    所以,能在代码执行完毕后,资源能够被正确地关闭或释放,即使在代码执行过程中发生异常也是如此。

    with open("hello.txt", "a") as f:
        f.write("hello world!")
    

    总结:文件会遇到异常,也都需要关闭,用with打开不论是否遇到异常,都能正确关闭。

    22. 字符串转列表 s.split(‘,’)

    >>> s = "1,2,3,4,5,6,7,8,9"
    >>> s.split(",")
    ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    

    字符串split返回列表

    23. 字符串转整数

    list(map(lambda x: int(x), s.split(",")))先转为列表,再用map遍历转int

    24.删除列表中的重复值

    先变成set集合再list,变过去再变回来,集合去重 list(set(mylist))

    25. 字符串单词统计

    统计字母个数用collection的counter

    >>> from collections import Counter
    >>> mystr = 'sdfsfsfsdfsd,were,hrhrgege.sdfwe!sfsdfs'
    >>> Counter(mystr)
    Counter({'s': 9,
             'd': 5,
             'f': 7,
             ',': 2,
             'w': 2,
             'e': 5,
             'r': 3,
             'h': 2,
             'g': 2,
             '.': 1,
             '!': 1})
    

    统计单词个数,先变为列表,在计算列表长度

    >>> mystr2 = "hello, Nice to meet you!"
    >>> len(mystr2.split(" "))
    5
    
    

    26. 列表推导,求奇偶数

    [x for x in range(20) if x % 2 == 1]

    27. 一行代码展开列表

    对于list1中的每个元素i(子列表),遍历其所有元素j,并将这些元素收集到一个新的列表中:list1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    [ j for i in list1 for j in i]

    28. 实现二分法查找函数

    //向下取整到最接近的整数
    非递归

    def binary_search(data, item):
        left = 0
        right = len(data) - 1
        while left <= right:
            mid = (left + right) // 2
            if data[mid] == item:
                return True
            elif data[mid] < item:
                left = mid + 1
            else:
                right = mid - 1
        return False
    
    

    递归

    def binary_search(data, item):
        n = len(data)
        if n > 0:
            mid = n // 2
            if data[mid] == item:
                return True
            elif data[mid] > item:
                return binary_search(data[:mid], item)
            else:
                return binary_search(data[mid + 1:], item)
        return False
    
    

    不管怎样,都是返回等于,更新是中间值-1和中间值+1

    29. 合并两个元组到字典(zip一对一打包)

    先zip再dict

    >>> a = ("zhangfei", "guanyu")
    >>> b = (66, 80)
    >>> dict(zip(a, b)) ## 
    {'zhangfei': 66, 'guanyu': 80}
    
    

    30. 给出如下代码的输入,并简单解释

    >>> a = (1, 2, 3, [4, 5, 6, 7], 8)
    >>> a[3] = 2
    

    ✘:()是元组,元组不能修改,不可变

    >>> a = (1, 2, 3, [4, 5, 6, 7], 8)
    >>> a[3][2] = 2
    >>> a
    (1, 2, 3, [4, 5, 2, 7], 8)
    

    ✔:列表可修改,就()元组不行

    32. 列表推导式[]、字典推导式{}和生成器表达式()

    列表推导式是方括号[],返回一个列表list [ k for k in range(10)]
    字典推导式是花括号还有键和值,返回一个字典dic

    >>> dic = {k: 2 for k in ["a", "b", "c", "d"]}
    >>> dic
    {'a': 2, 'b': 2, 'c': 2, 'd': 2}
    >>> type(dic)
    dict
    

    生成器是圆括号(),用next访问元素

    ge = (i for i in range(20))
    >>> ge_list
    <generator object <genexpr> at 0x000001C3C127AAC8>  #由一个生成器表达式(<genexpr>)创建,并存储在内存地址0x000001C3C127AAC8处
    next(ge)
    

    33. 简述 read、readline、readlines 的区别

    read() 读取整个文件
    readline() 读取下一行,使用生成器方法
    readlines() 读取整个文件到一个迭代器,需要一行一行遍历才能输出

    # 假设有一个名为 'example.txt' 的文件,内容如下:
    # Line 1
    # Line 2
    # Line 3
    
    with open('example.txt', 'r') as file:
        # 使用 read() 方法读取整个文件
        content = file.read()
        print(content)
    
        # 重置文件指针到开始位置
        file.seek(0)
    
        # 使用 readline() 方法逐行读取
        print(file.readline())  # 输出: 'Line 1\n'
        print(file.readline())  # 输出: 'Line 2\n'
    
        # 再次重置文件指针到开始位置
        file.seek(0)
    
        # 使用 readlines() 方法读取所有行
        lines = file.readlines()
        for line in lines:
            print(repr(line))  # 输出: 'Line 1\n', 'Line 2\n', 'Line 3\n'
    

    34. 打乱一个列表 andom.shuffle

    >>> import random
    >>> list = list(range(1, 10))
    >>> random.shuffle(list)
    >>> list
    [3, 9, 1, 4, 6, 2, 8, 7, 5]
    

    35. 反转字符串

    ‘luobodazahui’[::-1]表示以-1的步长向前遍历,相当于从后往前遍历一遍,也就是反转。而列表可以直接用reverse

    36. 单下划线和双下划线的作用

    37. 新式类和旧式类

    在 python 里凡是继承了 object的类,都是新式类
    Python3 里只有新式类
    (又是一个版本的问题)

    38. Python 面向对象中的继承有什么特点

    继承是一种创建新类的方式,在 Python 的面向对象编程中,继承具有以下特点:

    1. 基类(父类)与子类(子类):在继承关系中,被继承的类称为基类或父类,继承基类的类称为子类。子类会继承基类的属性和方法。

    2. 属性与方法继承:子类会继承基类的所有属性和方法,因此在子类中可以访问基类的属性和方法。根据继承方式的不同(如单继承、多重继承),子类可以继承多个基类的属性和方法。

    3. 扩展与覆盖:子类可以在继承基类的基础上进行扩展,添加新的属性和方法。同时,子类也可以覆盖(重写)基类的方法以实现不同的功能。当子类中定义了与基类同名的方法时,子类会优先调用自己的方法。

    4. super() 函数:在子类中,可以使用 super() 函数来调用基类的方法。这在子类需要在重写方法的基础上扩展基类功能时非常有用。使用 super() 函数可以确保基类的方法按照方法解析顺序(MRO)正确地调用。

    5. 方法解析顺序(MRO):Python 中使用 C3 线性化算法来解决多重继承的顺序问题。MRO 是一个包含类自身及其所有基类的有序列表,它决定了类继承体系中调用方法的顺序。可以使用 ClassName.mro()super() 函数查看 MRO 列表。

    6. 抽象基类(ABC):Python 中的抽象基类用于定义接口规范,它强制子类实现特定的方法。抽象基类可以通过 abc 模块的 ABCMeta 元类和 abstractmethod 装饰器来实现。

    下面是一个简单的继承示例:

    class Animal:
        def __init__(self, name):
            self.name = name
    
        def speak(self):
            pass
    
    class Dog(Animal):
        def speak(self):
            return f'{self.name} says Woof!'
    
    class Cat(Animal):
        def speak(self):
            return f'{self.name} says Meow!'
    
    animals = [Dog("Max"), Cat("Molly")]
    
    for animal in animals:
        print(animal.speak())
    

    在这个示例中,DogCat 类都继承了 Animal 类,并分别覆盖了 speak() 方法。

    39. super 函数的作用

    用于调用父类(超类)的方法或初始化父类(在__init__中)

    class Parent:
        def __init__(self, value):
            self.value = value
    
        def show(self):
            print(f"Parent value: {self.value}")
    
    class Child(Parent):
        def __init__(self, value, extension):
            super().__init__(value)  # 初始化父类的 __init__
            self.extension = extension
    
        def show(self):
            super().show()  # 调用父类的 show 方法
            print(f"Child extension: {self.extension}")
    
    # 创建 Child 类的实例
    child = Child(10, 'extra info')
    child.show()
    

    40. 类中的各种方法

    在类中,我们可以定义各种不同类型的函数,主要包括以下几种:

    1. 实例方法:这是类中最常见的函数类型,它们需要通过实例来调用,第一个参数默认为 self,代表实例本身,可以访问和修改实例的属性。
    class MyClass:
        def instance_method(self, a, b):
            return a + b
    
    1. 类方法:类方法是定义在类上的函数,通过 @classmethod 装饰器来修饰,它们的第一个参数默认为 cls,代表类本身,可以访问和修改类属性,但不能访问实例属性。
    class MyClass:
        class_variable = 123
    
        @classmethod
        def class_method(cls):
            cls.class_variable += 1
            return cls.class_variable
    
    1. 静态方法:静态方法是定义在类上的函数,通过 @staticmethod 装饰器来修饰,它们不需要特定的第一个参数,不能访问类或实例的任何属性。
    class MyClass:
        @staticmethod
        def static_method(a, b):
            return a * b
    
    1. 属性方法:属性方法可以让类的一个方法变成只读属性,或者为属性的读写提供特定的方法。通过 @property 装饰器实现。
    class MyClass:
        def __init__(self, value):
            self._value = value
    
        @property
        def value(self):
            return self._value
    
        @value.setter
        def value(self, new_value):
            if isinstance(new_value, int):
                self._value = new_value
            else:
                raise ValueError("Value must be an integer")
    
    1. 魔法方法:这些方法以双下划线开头和结尾,由 Python 自动调用,用于实现类的内部机制。比如 __init__, __del__, __str__, __eq__ 等。
    class MyClass:
        def __init__(self, value):
            self.value = value
    
        def __str__(self):
            return f"MyClass with value {self.value}"
    
        def __eq__(self, other):
            if isinstance(other, MyClass):
                return self.value == other.value
            return False
    

    以上就是类中常见的几种函数类型。

    41. 如何判断是函数还是方法

    与类和实例无绑定关系的 function都属于函数(function)
    与类和实例有绑定关系的 function都属于方法(method)

    42. isinstance 的作用以及与type()的区别(🧡🧡)

    区别:

    type()不会认为子类是一种父类类型,不考虑继承关系
    isinstance()会认为子类是一种父类类型,考虑继承关系

    class A(object):
        pass
    
    class B(A):
        pass
    
    >>> a = A()
    >>> b = B()
    
    >>> print(isinstance(a, A))
    True
    >>> print(type(a) == A)
    True
    
    >>> print(isinstance(b, A)) #考虑继承
    True
    >>> print(type(b) == A) #不考虑继承
    False
    

    43. 单例模式与工厂模式

    单例模式和工厂模式是两种常用的软件设计模式,它们在软件设计中扮演着不同的角色,具有各自的特点和适用场景。以下是两种模式的详细介绍:

    单例模式

  • 定义:单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
  • 特点
  • 唯一性:单例模式保证一个类在整个应用程序中只有一个实例。
  • 全局访问点:通过单例模式,可以提供一个全局访问点来访问该类的唯一实例。
  • 适用场景:适用于创建一个对象需要消耗过多资源,或者需要保证对象唯一性的场景,如数据库连接池、线程池等。
  • 实现方式:单例模式有多种实现方式,包括懒汉式、饿汉式、双重检查锁和登记式等。
  • 工厂模式

  • 定义:工厂模式是一种设计模式,它通过一个工厂类来封装和抽象对象的创建过程,使得客户端代码不需要直接进行对象的实例化。
  • 特点
  • 封装性:工厂模式封装了对象创建的细节,降低了系统各部分之间的耦合度。
  • 接口一致性:所有创建的对象通常都实现自同一个接口或继承自同一个基类,增加了代码的灵活性。
  • 适用场景:适用于需要生成复杂对象,或者需要灵活应对不同条件创建不同实例的场景,如数据库访问、日志记录等。
  • 实现方式:工厂模式有多种实现方式,包括简单工厂模式、工厂方法模式和抽象工厂模式。
  • 区别

  • 目的:单例模式主要用于控制类的实例数量,确保只有一个实例;而工厂模式主要用于封装对象的创建过程,提高代码的可维护性和可扩展性。
  • 实现方式:单例模式通过类的静态方法和私有构造函数来实现;工厂模式通过工厂类来创建对象实例。
  • 适用场景:单例模式适用于需要保证对象唯一性的场景;工厂模式适用于需要灵活创建不同对象实例的场景。
  • 通过以上分析,我们可以看到单例模式和工厂模式在设计模式中各有其独特的用途和优势,选择哪种模式取决于具体的应用场景和需求。

    44. 查看目录下的所有文件

    import os
    print(os.listdir('.'))  # 其中 '.' 表示当前工作目录。这个函数会返回一个列表,包含了当前目录下所有的文件和目录名。
    

    45. 计算由1到5组成的互不重复的三位数

    for i in range(1,6):
    	for j  in range(1,6):
    		for k in range(1,6):
    			if i != j and j != k  and k !=i:
    			print(f"{i}{j}{k}")
    

    46. 去除字符串首尾空格

    .strip(删除字符串两端的空白字符或者指定字符

    >>> "   hello world    ".strip()
    'hello world'
    
    

    47. 去除字符串中间的空格

    >>> "hello you are good".replace(" ", "")
    'helloyouaregood'
    >>> "".join("hello you are good".split(" "))
    'helloyouaregood'
    

    48. 字符串格式化方式

    方法一:使用 % 操作符

    print(“This is for %s” % “Python”)
    print(“This is for %s, and %s” %(“Python”, “You”))

    方法二:str.format(在 Python3 中,引入了这个新的字符串格式化方法)

    print(“This is my {}”.format(“chat”))
    print(“This is {name}, hope you can {do}”.format(name=“zhouluob”, do=“like”))

    方法三:f-strings(在 Python3-6 中,引入了这个新的字符串格式化方法)

    name = “luobodazahui”
    print(f"hello {name}")

    49. 将"hello world"转换为首字母大写"Hello World"

    1 对字符串.title()函数 str1 = “hello world”
    str1.title()
    2 .对单词capitalize()函数
    " ".join(list(map(lambda x: x.capitalize(), str1.split(" "))))
    3 对字符.upper()
    " ".join(list(map(lambda x: x[0].upper() + x[1:], str1.split(" "))))

    map()函数将这个函数应用到分割后的单词列表的每个元素上

    50. 一行代码转换列表中的整数为字符串

    >>> list1 = [1, 2, 3]
    >>> list(map(lambda x: str(x) for x in list1))# 或者 [lambda x: str(x) for x in list1]列表推导式
    ['1', '2', '3']
    

    51. Python 中的反射(🧡🧡)27号

    基于字符串的事件驱动!利用字符串的形式对模块的内存进行修改
    getattr,hasattr,setattr,delattr 对模块的修改都在内存中进行,并不会影响文件中真实内容。

    52. metaclass 元类(🧡🧡)

    type就是创建object类对象的类,obeject是type的实例,创建一个类其实是type的__call__运算符的重载

    正常的 MyClass 定义,和手工去调用type运算符的结果是一样的

    class X:
        a = 1
        
    X = type('X', (object,), dict(a=1))
    

    那元类是干嘛的呢?
    是type的子类,通过替换type中的__call__运算符重载机制,可以实现父类对子类的属性进行操作。

    class MyMeta(type):
        def __new__(cls, *args, **kwargs):
            print('===>MyMeta.__new__')
            print(cls.__name__)
            return super().__new__(cls, *args, **kwargs)
     
        def __init__(self, classname, superclasses, attributedict):
            super().__init__(classname, superclasses, attributedict)
            print('===>MyMeta.__init__')
            print(self.__name__)
            print(attributedict)
            print(self.tag)
     
        def __call__(self, *args, **kwargs):
            print('===>MyMeta.__call__')
            obj = self.__new__(self, *args, **kwargs)
            self.__init__(self, *args, **kwargs)
            return obj
     
        
    class Foo(object, metaclass=MyMeta):
        tag = '!Foo'
     
        def __new__(cls, *args, **kwargs):
            print('===>Foo.__new__')
            return super().__new__(cls)
     
        def __init__(self, name):
            print('===>Foo.__init__')
            self.name = name
    # ----------------------输出----------------------
    # ===>MyMeta.__new__
    # MyMeta
    # ===>MyMeta.__init__
    # Foo
    # {'__module__': '__main__', '__qualname__': 'Foo', 'tag': '!Foo', '__new__': <function Foo.__new__ at 0x000001B1B2379678>, '__init__': <function Foo.__init__ at 0x000001B1B2379708>, '__classcell__': <cell at 0x000001B1B23880A8: MyMeta object at 0x000001B1B1A509A8>}
    # !Foo
     
    >>> print('test start')
    >>> foo = Foo('test')
    >>> print('test end')
    # test start
    # ===>MyMeta.__call__
    # ===>Foo.__new__
    # ===>Foo.__init__
    # test end
    

    创建一个类时,(1)先在属性中查找是否有metaclass属性,(2)没有,就在父类中查找(3)没有就在模块中查找(4)再没有,就用type来创建对象。
    注意,通过metaclass,必须返回一个类return obj
    同时,可以实现在创建时,就对子类的属性进行操作。(一般父类是不可以对子类的属性操作的哦)

    def MyMetaFunction(classname, superclasses, attributedict):
        attributedict['year'] = 2019
        return type(classname, superclasses, attributedict)
     
     
    class Foo(object, metaclass=MyMetaFunction):
        tag = '!Foo'
     
        def __new__(cls, *args, **kwargs):
            print('===>Foo.__new__')
            return super().__new__(cls)
     
        def __init__(self, name):
            print('===>Foo.__init__')
            self.name = name
     
    >>> foo = Foo('test')
    ===>Foo.__new__
    ===>Foo.__init__
    >>> print('name:%s,tag:%s,year:%s' % (foo.name, foo.tag, foo.year))
    name:test,tag:!Foo,year:2019
    
    

    先有元类,再有类,再有实例。

    53. sort 和 sorted 的区别

    sort改变原列表。sorted返回一个新的对象

    54. Python 中的 GIL

    GIL 是 Python 的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行 Python 程序的时候会占用 Python 解释器(加了一把锁即 GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。

    进程(Process)

  • 定义:进程是操作系统进行资源分配和调度的一个独立单位。它是应用程序运行的实例,拥有独立的内存空间。
  • 地址空间:每个进程有自己的地址空间,这包括代码段、数据段和堆栈等。
  • 全局数据:进程拥有自己的全局变量和静态变量。
  • 独立性:进程之间相互独立,一个进程的崩溃不会直接影响到其他进程。
  • 资源占用:进程间通信(IPC)和进程切换开销较大,因为它们需要操作系统进行协调。
  • 创建和销毁开销:进程的创建和销毁需要较多的资源和时间。
  • 线程(Thread)

  • 定义:线程是进程中的一个实体,是CPU调度和执行的单位,是程序执行的最小单位。
  • 地址空间:同一进程内的线程共享进程的地址空间,包括代码段和数据段。
  • 全局数据:线程共享相同的全局变量和静态变量。
  • 轻量级:线程的创建、同步和销毁的开销比进程小。
  • 并发性:线程使得多任务并发执行成为可能,它们可以并行处理任务。
  • 资源共享:线程之间可以轻松共享数据,但这也可能导致数据同步问题。
  • 进程与线程的区别:

    1. 资源占用:进程拥有独立的资源,线程共享进程的资源。
    2. 执行:进程是程序的执行流程,线程是进程中执行的子流程。
    3. 创建开销:进程的创建和销毁开销较大,线程相对较小。
    4. 通信:进程间通信需要使用IPC机制,线程间可以直接访问共享内存。
    5. 独立性:进程间独立,线程间共享。
    6. 错误影响:进程的一个错误通常不会影响其他进程,但线程的错误可能影响整个进程。
    7. 上下文切换:线程间的上下文切换比进程快,因为它们共享同样的内存空间。

    理解示例:

  • 进程可以比作一个工厂,每个工厂拥有自己的资源和生产流程。
  • 线程可以比作工厂中的工人,他们共享工厂的资源,协同工作以完成生产任务。
  • 55. 产生8位随机密码(🧡🧡)

    random.choice选择,string.printable生成字符串

    >>> import random
    >>> import string
    >>> "".join(random.choice(string.printable[:-7]) for i in range(8)) # 从 string.printable 中移除最后 7 个字符
    
    

    生成器表达式和列表推导式(list comprehension)都是简洁地创建新数据结构的方法,但它们之间存在一些关键区别:

    1. 内存使用:列表推导式会立即生成一个新的列表,这意味着它会占用与列表大小成正比的内存。对于大型数据集,这可能导致内存不足。而生成器表达式不会立即生成值,而是返回一个生成器对象。这个对象在迭代过程中逐个产生值,因此它的内存占用很小,无论它表示的序列有多大。

    2. 语法:生成器表达式的语法与列表推导式非常相似,只是在圆括号而不是方括号内编写。例如:

    3. 列表推导式:[expr for item in iterable if condition]
    4. 生成器表达式:(expr for item in iterable if condition)
    5. 返回类型:列表推导式返回一个列表,而生成器表达式返回一个生成器对象(generator 类型)。生成器对象实现了迭代器协议,可以使用 next() 函数或 for 循环进行迭代。

    6. 可迭代性:生成器表达式返回的生成器对象只能迭代一次。当所有值都被迭代完后,生成器对象就会耗尽。要再次迭代,需要重新创建一个新的生成器对象。而列表推导式返回的列表可以多次迭代。

    以下是一个简单的例子,展示了列表推导式和生成器表达式的用法:

    
    # 列表推导式 squares = [random.randint(1, 100) ** 2 for _ in range(5)] print(squares)
    
    # 生成器表达式 squares_gen = (random.randint(1, 100) ** 2 for _ in range(5)) for square in squares_gen:
        print(square, end=" ") ```
    

    56. 输出原始字符

    >>> print('hello\nworld')
    hello
    world
    >>> print(b'hello\nworld')
    b'hello\nworld'
    >>> print(r'hello\nworld')
    hello\nworld
    

    b 前缀表示字节字符串(byte string),即 b’hello\nworld’ 是一个包含字节的不可变序列。这种字符串用于处理二进制数据。
    r 前缀表示原始字符串(raw string),在这种字符串中,转义字符不会被处理。

    57. 简述 any() 和 all() 方法

    all 可迭代对象中的所有元素是否都为 True
    any 函数用于判断可迭代对象中是否至少有一个元素为 True
    0 none false都是false

    58. 反转整数

    是否是整数,是否一位数,是否负数

     def reverse_int(x):
         if not isinstance(x, int):
             return False
         if -10 < x < 10:
             return x
         tmp = str(x)
         if tmp[0] != '-':
             tmp = tmp[::-1]
             return int(tmp)
        else:
            tmp = tmp[1:][::-1]
            x = int(tmp)
            return -x
    
    

    59. 函数式编程(🧡🧡)

    函数式编程是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。由于 Python 允许使用变量,因此,Python 不是纯函数式编程语言

    函数式编程允许参数是函数,也允许返回一个函数

    60. 简述闭包(🧡🧡)

    如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。允许函数“记住”其被创建时的环境状态,即使外层函数已经执行完毕
    闭包特点:

    必须有一个内嵌函数;
    内嵌函数必须引用外部函数中的变量;
    外部函数的返回值必须是内嵌函数。

    闭包的应用场景: 柯里化(Currying):将一个多参数的函数转换成多个单参数函数。 数据隐藏:通过闭包实现封装和数据隐藏。
    装饰器:Python中的装饰器就是一个典型的闭包应用。 迭代器和生成器:迭代器和生成器的 next 方法经常使用闭包来保持迭代状态。

    61. 简述装饰器(🧡🧡)

    装饰器是一个嵌套函数,不需要修改被装饰函数的源代码,用于给被装饰的函数修改或添加功能,内部的wrapper函数替代原始函数。
    被装饰函数作为参数传入装饰器,内部函数替代原始函数执行后还可以添加一些功能。
    广泛应用于缓存、权限校验(如django中的@login_required和@permission_required装饰器)、性能测试(比如统计一段程序的运行时间)和插入日志等应用场景

    基本用法:

    1. 定义装饰器

    2. 创建一个装饰器函数,通常接受一个函数作为参数。
    3. 包装函数

    4. 在装饰器内部,定义一个包装函数(通常命名为 wrapper),它将被返回以替代原始函数。
    5. 调用原始函数

    6. 在包装函数中,你可以添加额外的逻辑,然后调用原始函数。
    7. 返回包装函数

    8. 装饰器返回包装函数,这样当原始函数被调用时,实际上是调用了包装函数。

    示例:

    def my_decorator(func):
        def wrapper():
            print("Something is happening before the function is called.")
            func()
            print("Something is happening after the function is called.")
        return wrapper
    
    @my_decorator
    def say_hello():
        print("Hello!")
    
    say_hello()
    

    输出:

    Something is happening before the function is called.
    Hello!
    Something is happening after the function is called.
    

    在这个例子中,my_decorator 是一个装饰器,它接受一个函数 func 作为参数,并返回一个新的函数 wrappersay_hello 函数被 @my_decorator 装饰,这意味着 say_hello 函数被 my_decorator 包装。

    带参数的装饰器:

    装饰器也可以接受参数。为了实现这一点,你可以在装饰器外层再定义一个函数,该函数接受参数并返回实际的装饰器。

    def repeat(num_times):
        def decorator(func):
            def wrapper(*args, **kwargs):
                for _ in range(num_times):
                    result = func(*args, **kwargs)
                return result
            return wrapper
        return decorator
    
    @repeat(num_times=3)
    def greet(name):
        print(f"Hello, {name}!")
    
    greet("World")
    

    输出:

    Hello, World!
    Hello, World!
    Hello, World!
    

    在这个例子中,repeat 函数接受一个参数 num_times,然后返回一个装饰器 decorator,该装饰器接受一个函数并返回一个新的包装函数 wrapper

    装饰器的用途:

  • 日志记录:在函数执行前后添加日志记录。
  • 性能测试:测量函数执行时间。
  • 事务处理:确保数据库事务的一致性。
  • 权限检查:在执行函数前检查用户权限。
  • 缓存:缓存函数的结果以提高性能。
  • 62. 协程的优点

    63. 实现斐波那契数列

    64. 正则切分字符串

    items = re.split(pattern, text)
    正则表达式(Regular Expression,简称regex)是一种强大的文本处理工具,用于搜索、替换、切分或匹配字符串中的模式。在Python中,正则表达式通过内置的re模块提供支持。

    使用正则表达式切分字符串的基本步骤:

    1. 导入re模块

      import re
      
    2. 编写正则表达式

    3. 正则表达式是一个字符串,包含了用于定义搜索模式的字符和特殊符号。
    4. 使用re.split()方法

    5. re.split(pattern, string):使用正则表达式pattern来切分字符串string
    6. 获取结果

    7. re.split()返回一个列表,其中包含了被正则表达式定义的模式切分后的子字符串。

    示例:

    假设我们有一个字符串,包含了用逗号分隔的数值,我们想要将这些数值单独切分出来:

    import re
    
    text = "apple,101,banana,202,cherry,303"
    # 正则表达式:非数字字符(,)
    pattern = r'[^\d]+'
    
    # 使用正则表达式切分字符串
    items = re.split(pattern, text)
    print(items)  # 输出: ['apple', '101', 'banana', '202', 'cherry', '303']
    

    在这个例子中,[^\d]+是一个正则表达式,表示匹配一个或多个非数字字符。re.split()使用这个模式来切分字符串text

    注意事项:

  • 正则表达式需要根据具体需求来编写,不同的模式会有不同的切分效果。
  • 特殊字符(如*.?[ ]( )等)在正则表达式中有特殊含义,如果需要匹配这些字符本身,通常需要使用反斜杠\进行转义。
  • 使用正则表达式时,要注意其性能,复杂的正则表达式可能会影响程序的执行效率。
  • 正则表达式是一种非常灵活和强大的文本处理工具,掌握它可以大大提高处理字符串的效率。

    65. yield 用法

    67. 冒泡排序

    68. 快速排序

    快速排序(Quick Sort)是一种高效的排序算法,由英国计算机科学家 Tony Hoare 于 1960 年代提出

    快速排序的工作原理如下:

    1. 选择一个基准元素(pivot),可以是列表的任意元素。通常选择列表的第一个元素、最后一个元素或中间元素作为基准。
    2. 将列表中小于基准元素的值移动到基准的左侧;将大于基准元素的值移动到基准的右侧。这个过程称为分区(partitioning)操作。经过分区后,基准元素将位于其最终排序位置。
    3. 对基准元素左侧和右侧的子列表进行递归处理,分别选择左侧子列表和右侧子列表的基准元素并进行分区。
    4. 递归终止条件是处理的子列表只有一个元素或为空。

    以下是一个使用 Python 实现的快速排序示例:

    def quick_sort(arr):
        if len(arr) <= 1:
            return arr
        pivot = arr[len(arr) // 2]
        left = [x for x in arr if x< pivot]
        middle = [x for x in arr if x == pivot]
        right = [x for x in arr if x > pivot]
        return quick_sort(left) + middle + quick_sort(right)
    
    # 示例
    arr = [64, 34, 25, 12, 22, 11, 90]
    sorted_arr = quick_sort(arr)
    print("Sorted array is:", sorted_arr)
    

    输出结果:

    Sorted array is: [11, 12, 22, 25, 34, 64, 90]
    

    快速排序的平均时间复杂度为 O(nlog(n)),最坏情况下的时间复杂度为 O(n^2)。但在实际应用中,快速排序通常比其他 O(nlog(n)) 算法更高效,因为它的内部循环可以在大部分架构上更有效地利用缓存。为了避免最坏情况的发生,可以使用随机方法选择基准元素。

    70. requests 简介

    71. 比较两个 json 数据是否相等

    72. 读取键盘输入 input()

    def forinput():
        input_text = input()
        print("your input text is: ", input_text)
        
    >>> forinput()
    >? 2
    your input text is:  2
    

    73. enumerate()的用法

    74. pass 语句

    占位,不做操作,为了保持结构完整性

    def forpass(n):
        if n == 1:
            pass
        else:
            print('not 1')
            
    >>> forpass(1)
    
    

    75. 正则匹配邮箱

    76. 统计字符串中大写字母的数量

    ·if i.isupper()

    77. json 序列化时保留中文

    ensure_ascii=False

    78. 简述继承

    79. 什么是猴子补丁(🧡🧡)

    猴子补丁(Monkey Patch)和装饰器(Decorator)都是 Python 中用于修改或增强已有对象功能的技术,但它们的用途、实现方式以及适用场景有所不同

    猴子补丁(Monkey Patch)

    猴子补丁是在运行时动态地修改类或模块的行为。通过直接修改类或模块的属性,可以添加、修改或删除现有的方法、属性等。猴子补丁的主要优点是可以在不修改原始代码的情况下调整行为,这在调试、测试和扩展第三方库时非常有用。然而,猴子补丁可能导致代码难以维护和理解,因为它改变了对象的内部实现。另外,使用猴子补丁时需要谨慎,因为在某些情况下,它可能导致意外的副作用或不可预见的错误。

    以下是一个猴子补丁的示例,用于修改字符串类的 upper 方法:

    def custom_upper(self):
        return self[::-1].upper()
    
    str.upper = custom_upper
    
    text = "hello"
    print(text.upper())  # 输出 "OLLEh"
    

    装饰器(Decorator)

    装饰器是一种设计模式,用于在不修改原始函数或类的基础上,为其添加新功能或者修改现有功能。装饰器本质上是一个接受函数或类作为参数的高阶函数,它返回一个新的函数或类,以实现对输入对象功能的扩展或修改。装饰器语法使用 @decorator_name 语法糖,并放在要修饰的函数或类定义之前。

    以下是一个使用装饰器记录函数执行时间的示例:

    import time
    
    def timer_decorator(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            print(f"{func.__name__} took {end_time - start_time:.2f} seconds to execute.")
            return result
        return wrapper
    
    @timer_decorator
    def some_function():
        time.sleep(1)
    
    some_function()
    

    总结:

  • 猴子补丁是在运行时动态修改对象的行为,而装饰器是通过高阶函数在编译时修改或增强对象的功能。
  • 猴子补丁主要适用于对模块或类的内部实现进行修改,而装饰器主要用于修改或增强函数或类的功能,而不改变其内部实现。
  • 装饰器提供了更好的封装、可维护性和可读性,而猴子补丁可能导致代码变得难以理解和维护。
  • 尽量使用装饰器来实现功能扩展,而避免使用猴子补丁,除非确实需要在运行时修改对象的行为。
  • 80. help() 函数和 dir() 函数

    81. 解释 Python 中的//,%和**运算符

    82. 主动抛出异常

    raise

    def test_raise(n):
        if not isinstance(n, int):
            raise Exception("not a int type")
        else:
            print("good")
            
    >>> test_raise(8.9)
    Traceback (most recent call last):
      File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_code
        exec(code_obj, self.user_global_ns, self.user_ns)
      File "<ipython-input-106-27962090d274>", line 1, in <module>
        test_raise(8.9)
      File "<ipython-input-105-8095f403c7e2>", line 3, in test_raise
        raise Exception("not a int type")
    Exception: not a int type
    
    

    83. tuple 和 list 转换

    tuple () list()

    >>> tuple1 = (1, 2, 3, 4)
    >>> list1 = list(tuple1)
    >>> print(list1)
    >>> tuple2 = tuple(list1)
    >>> print(tuple2)
    

    84. 简述断言

    Python 的断言就是检测一个条件,如果条件为真,它什么都不做;反之它触发一个带可选错误信息的 AssertionError。

    85. 什么是异步非阻塞

    86. 什么是负索引

    87. 退出 Python 后,内存是否全部释放

    88. Flask 和 Django 的异同

    89. 创建删除操作系统上的文件

    90. 简述 logging 模块

    91. 统计字符串中字符出现次数

    92. 正则 re.complie 的作用

    93. try except else / finally 的意义

    94. 反转列表

    95. 字符串中数字替换

    >>> import re
    >>> str1 = '我是周萝卜,今年18岁'
    >>> re.sub(r"\d+", "20", str1)
    '我是周萝卜,今年20岁'
    
    

    96. 读取大文件(🧡🧡)

    97. 输入日期, 判断这一天是这一年的第几天

    98. 排序

    99. 将字符串处理成字典

    100. 下面代码的输出结果将是什么?

    补充 被问的

    101 python协程 异步

    协程 线程 进程都属于并发;并发是在一个处理器上轮流处理任务,强调资源的共享和分配;并行是在多个处理器上同时处理任务,强调硬件资源的利用。

    asyncio:异步函数库
    request : 同步函数库

    详细见metagpt异步编程

    102 python子类不用super调用父类属性

    不用super一般是不可以调用父类的方法的,但是子类执行行,父类执行__new__ 函数(创建实例),所以想不用super调用父类可以写到__new__里

    问题参考自python八股

    作者:小霖同学onism

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python笔试面试要点详解(自用版)

    发表回复