Python面试题70-87高级特效

参考自:https://cloud.tencent.com/developer/article/1490616(题目来源)
部分答案引自:https://blog.csdn.net/xiongchengluo1129/article/details/80462651

所有题目

高级特效

70.函数装饰器有什么作用?请列举说明?

#装饰器就是增强函数或类的功能的一个函数。
#它可以在不需要做任何代码变动的前提下给一个函数增加额外功能,启动装饰的效果。 它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
#案例 计算代码执行时间
import time

#定义装饰器
def time_count(func):
    def wrapper(*args,**kargs):
        start_time=time.time()
        f=func(*args,**kargs)
        exec_time=time.time()-start_time
        print(exec_time)
        return f
    return wrapper

#使用装饰器
@time_count
def add(a,b):
    return a+b

@time_count
def sub(a,b):
    return a-b

71.Python 垃圾回收机制?

python中对象的类型和内存都是在运行时确定的
python垃圾回收机制主要体现在以下三个方面:
1.引用计数机制 2.标记-清除 3.分代回收
1.
Python垃圾回收主要以引用计数为主,分代回收为辅。引用计数法的原理是每个对象维护一个ob_ref,用来记录当前对象被引用的次数,也就是来追踪到底有多少引用指向了这个对象,

当发生以下四种情况的时候,该对象的引用计数器+1
对象被创建  a=14
对象被引用  b=a
对象被作为参数,传到函数中   func(a)
对象作为一个元素,存储在容器中   List={a,”a”,”b”,2}

当发生以下四种情况时,该对象的引用计数器-1
当该对象的别名被显式销毁时  del a
当该对象的引别名被赋予新的对象,   a=26
一个对象离开它的作用域,例如 func函数执行完毕时,函数里面的局部变量的引用计数器就会减一(但是全局变量不会)
将该元素从容器中删除时,或者容器被销毁时。
存在问题:
-维护引用计数消耗资源,维护引用计数的次数和引用赋值成正比,而不像mark and sweep(标记-清除)等基本与回收的内存数量有关。
-无法解决循环引用的问题。A和B相互引用而再没有外部引用A与B中的任何一个,它们的引用计数都为1,但显然应该被回收。
2.
『标记清除(Mark—Sweep)』算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。那么GC又是如何判断哪些是活动对象哪些是非活动对象的呢?

对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。

3.
分配内存
-> 发现超过阈值了
-> 触发垃圾回收
-> 将所有可收集对象链表放到一起
-> 遍历, 计算有效引用计数
-> 分成 有效引用计数=0 和 有效引用计数 > 0 两个集合
-> 大于0, 放入到更老一代
-> =0, 执行回收
-> 回收遍历容器内的各个元素, 减掉对应元素引用计数(破掉循环引用)
-> 执行-1的逻辑, 若发现对象引用计数=0, 触发内存回收
-> python底层内存管理机制回收内存
详见:https://blog.csdn.net/xiongchengluo1129/article/details/80462651

72.魔法函数 __call__怎么使用?

#_call_ 可以把类实例当做函数调用
class Bardef __call__(self, *args, **kwargs)print('in call')
 
 
if __name__ == '__main__':
    b = Bar()
    b()

73.如何判断一个对象是函数还是方法?

1:
通过眼睛看 通过类方法调用为函数,通过实例化对象调用为方法
大致分为 类中的函数为方法,类外面声明def为函数(不准确)
法2:让计算机判断
from types import MethodType,FunctionType
使用
isinstance(需要判断的函数或方法,FunctionType)
isinstance(需要判断的函数或方法,MethodType)

74.@classmethod 和@staticmethod 用法和区别

一般来说,要使用某个类的方法,需要**先实例化一个对象**再调用方法。而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。

区别:
从它们的使用上来看,@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
@classmethod也不需要self参数,**但第一个参数需要是表示自身类的cls参数**。
如果在@staticmethod中要调用到这个类的一些属性方法,**只能直接类名.属性名或类名.方法名**。
而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
@staticmethod 经常有一些跟类有关系的功能但在运行时又不需要实例和类参与的情况下需要用到静态方法

参考:https://www.bilibili.com/video/BV13741167D8?from=search&seid=15464209750568307622

75.Python 中的接口如何实现?

接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。
但是在 Python 中根本就没有一个叫做 interface 的关键字,如果非要去模仿接口的概念,可以使用抽象类来实现。
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。使用 abc 模块来实现抽象类。
参考:https://blog.csdn.net/weixin_42181824/article/details/81874725

76.Python 中的反射了解么?

Python 的反射机制设定较为简单,一共有四个关键函数分别是 
getattr(获取对象中的方法或变量的内存地址)
hasattr(判断对象中是否有这个方法或变量)
setattr(为对象添加变量或方法)
delattr(删除对象中的变量。注意:不能用于删除方法)
它的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动。
拓展:
python提供了一个特殊的方法:__import__(字符串参数)。
通过它,我们就可以实现类似的反射功能。__import__()方法会根据参数,动态的导入同名的模块。对于导入目录报错,可使用参数fromlist = True

77.metaclass 作用?以及应用场景?


metaclass 即元类,metaclass 是类似创建类的模板,所有的类都是通过他来 create 的(调用new),
这使得你可以自由的控制创建类的那个过程,实现你所需要的功能。 
我们可以使用元类创建单例模式和实现 ORM 模式。
!!看不太明白
参见:https://blog.csdn.net/weixin_33943836/article/details/86330725

78.hasattr() getattr() setattr()的用法

hasattr(object, name)
判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回Falsegetattr(object, name[,default])
获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选。
需要注意的是,如果是获取的是对象的方法,返回的则是方法的内存地址,如果需要运行这个方法,
可以在后面添加一对括号。

setattr(object, name, values)
给对象的属性赋值,若属性不存在,先创建再赋值。
#案例
>>> class test():
    name="张三"
    def run(self):
            return "HelloWord"

>>> t=test()
>>> getattr(t, "age")
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    getattr(t, "age")
AttributeError: 'test' object has no attribute 'age'
>>> getattr(t, "age", setattr(t, "age", "18"))
'18'
>>> getattr(t, "age")
'18'
>>> hasattr(t,'name')
True

79.请列举你知道的 Python 的魔法方法及用途。

最常用三个:"__init_""_new__""__del__"
1.__new__是用来创建类并返回这个类的实例,__new__是对象实例化时第一个调用的方法,它只取下 cls 参数,并把其他参数传给 __init__ 。 
__new__很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候

2.__init__将传入的参数来初始化该实例,以及初始化示例属性,与new共同构成了“构造函数”, __init__在 Python 的类定义中用的最多

3.__del__将实例化后的对象销毁,即为析构函数

__call__允许一个类像函数一样被调用
案例:
class Person(object):
  def __init__(self, name, age):
    self.name = name
    self.age = age
    self.instance = add
  def __call__(self,*args):
    return self.instance(*args)
def add(args):
  return args[0] + args[1]
a = Person('p1', 20)
print(a([1,2]))
#这里将打印 3
#可见当创建a这个对象之后,如果定义了__call__函数则对象是可以像函数一样调用的。

上下文管理器:__enter__和__exit__
详见:https://blog.csdn.net/qq_37482956/article/details/100056517

迭代器方法:__iter__和__next__
__iter__:返回一个容器迭代器,很多情况下会返回迭代器,尤其是当内置的iter()方法被调用的时候,以及当使用for x in container:方式循环的时候。迭代器是它们本身的对象,它们必须定义返回self的iter方法。
__next__:返回迭代器的下一个元素
详见:https://blog.csdn.net/liweibin1994/article/details/77374854

80.如何知道一个 Python 对象的类型?

type(对象)

81.Python 的传参是传值还是传址?

python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。
如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。
如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值'来传递对象。

82.Python 中的元类(metaclass)使用举例

#使用元类实现一个单例模式
看不懂元类还可以参考:
https://www.cnblogs.com/Simon-xm/p/4034416.html
https://blog.csdn.net/jiahaowanhao/article/details/79293546
class Singleton(type):
    def __init__(self, *args, **kwargs):
        print("in __init__")
        self.__instance = None
        super(Singleton, self).__init__(*args, **kwargs)
 
    def __call__(self, *args, **kwargs):
        print("in __call__")
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance
 
class Foo(metaclass=Singleton):
    pass  # 在代码执行到这里的时候,元类中的__new__方法和__init__方法其实已经被执行了,而不是在 Foo 实例化的时候执行。且仅会执行一次。
 
foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2)

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

any(x)判断x对象是否为空对象,如果都为空、0、false,则返回false,如果不都为空、0、false,则返回true

all(x)如果all(x)参数x对象的所有元素不为0''False或者x为空对象,则返回True,否则返回False

84.filter 方法求出列表所有奇数并构造新列表,a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

#function -- 判断函数。iterable -- 可迭代对象。
filter(function, iterable)
a =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[i for i in a if i%2==1 ] #不使用filter
list(filter(lambda x :x%2==1, a))#使用filter

85.什么是猴子补丁?

猴子补丁(monkey patching):在运行时动态修改模块、类或函数,通常是添加功能或修正缺陷。
猴子补丁在代码运行时内存中)发挥作用,不会修改源码,因此只对当前运行的程序实例有效。
因为猴子补丁破坏了封装,而且容易导致程序与补丁代码的实现细节紧密耦合,所以被视为临时的变通方案,不是集成代码的推荐方式。
大概是下面这样的一个效果
def post():
    print("this is post")
    print("想不到吧")

class Http():
    @classmethod
    def get(cls):
        print("this is get")

    @staticmethod
    def get2():
        print("this is get")

def main():
    Http.get=post #动态的修改了 get 原因的功能,

if __name__ == '__main__':
    main()      
    Http.get()

86.在 Python 中是如何管理内存的?

1)垃圾回收
(2)引用计数
(1)(2#见题713)内存池机制
Python的内存机制以金字塔行,-1-2层主要有操作系统进行操作,

  第0层是C中的malloc,free等内存分配和释放函数进行操作;

  第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实现,当对象小于256K时有该层直接分配内存;

  第3层是最上层,也就是我们对Python对象的直接操作;

在 C 中如果频繁的调用 malloc 与 free 时,是会产生性能问题的.再加上频繁的分配与释放小块的内存会产生内存碎片. Python 在这里主要干的工作有:

  如果请求分配的内存在1~256字节之间就使用自己的内存管理系统,否则直接使用 malloc.

  这里还是会调用 malloc 分配内存,但每次会分配一块大小为256k的大块内存.

  经由内存池登记的内存到最后还是会回收到内存池,并不会调用 C 的 free 释放掉.以便下次使用.
  对于简单的Python对象,例如数值、字符串,元组(tuple不允许被更改)采用的是复制的方式(深拷贝?),
  也就是说当将另一个变量B赋值给变量A时,虽然A和B的内存空间仍然相同,但当A的值发生变化时,会重新给A分配空间,A和B的地址变得不再相同

87.当退出 Python 时是否释放所有内存分配?

不是的,循环引用其他对象或引用自全局命名空间的对象的模块,在 Python 退出时并非完全释放。
 
另外,也不会释放 c 库保留的内存部分
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值