Python 常见的 170 道面试题(49-69 操作类题目)

70.函数装饰器有什么作用?请列举说明?
  • 装饰器就是一个函数,它可以在不需要做任何代码变动的前提下给一个函数增加额外功能,启动装饰的效果。 它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
  • 基于python实现一个装饰器
    
    
71.Python 垃圾回收机制?

Python 不像 C++,Java 等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对 Python 语言来讲,对象的类型和内存都是在运行时确定的。这也是为什么我们称 Python 语言为动态类型的原因。
主要体现在下面三个方法:
1.引用计数机制 2.标记-清除 3.分代回收

72.魔法函数 __call__怎么使用?
# _call_ 可以把类实例当做函数调用
class Bar:
    def __call__(self, *args, **kwargs):
        print('in call')

if __name__ == '__main__':
    b = Bar()
    b()
73.如何判断一个对象是函数还是方法?

通常我们认为在类中的函数为方法,类外面声明def为函数,更严谨的说法是,通过类方法调用为函数,通过实例化对象调用为方法

  • 可以通过内置的isinstance 来判断
class Work(object):
   def show(self):
       print("执行show方法")

work = Work()

from types import MethodType,FunctionType
print(isinstance(Work.show,FunctionType))
print(isinstance(Work.show,MethodType))
print(isinstance(work.show,FunctionType))
print(isinstance(work.show,MethodType))

>>> True
>>> False
>>> False
>>> True
74.@classmethod 和@staticmethod 用法和区别
  • 一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
    而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
    • 这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
    • 既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢
      从它们的使用上来看,
      • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
      • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
      • 如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
      • @classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
75.Python 中的接口如何实现?
  • 接口的作用是提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。
  • 但是在 Python 中根本就没有一个叫做 interface 的关键字,
  • 如果非要去模仿接口的概念,可以使用抽象类来实现。
    • 抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。使用 abc 模块来实现抽象类。
76.Python 中的反射了解么?
  • 什么是反射机制:
    • 反射就是通过字符串的形式,导入模块;通过字符串的形式,去模块寻找指定函数,并执行。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!
    • getattr()函数是Python自省的核心函数
    • hasattr(object, name)
      • 说明:判断对象object是否包含名为name的特性(hasattr是通过调用getattr(ojbect, name)是否抛出异常来实现的)
    • setattr(object, name, value)
      • 这是相对应的getattr()。参数是一个对象,一个字符串和一个任意值。字符串可能会列出一个现有的属性或一个新的属性。这个函数将值赋给属性的。该对象允许它提供。例如,setattr(x,“foobar”,123)相当于x.foobar = 123。
    • delattr(object, name)
      • 与setattr()相关的一组函数。参数是由一个对象(记住python中一切皆是对象)和一个字符串组成的。string参数必须是对象属性名之一。该函数删除该obj的一个由string指定的属性。delattr(x, ‘foobar’)=del x.foobar
        可以通过上述函数,对模块进行一系列的操作:
r = hasattr(commons,xxx)判断某个函数或者变量是否存在
print(r)  
setattr(commons,'age',18)  给commons模块增加一个全局变量age = 18,创建成功返回none
setattr(config,'age',lambda  a:a+1)  //给模块添加一个函数
delattr(commons,'age')//删除模块中某个变量或者函数
  • getattr,hasattr,setattr,delattr对模块的修改都在内存中进行,并不会影响文件中真实内容
77.metaclass 作用?以及应用场景?

metaclass 即元类,metaclass 是类似创建类的模板,所有的类都是通过他来 create 的(调用new),这使得你可以自由的控制创建类的那个过程,实现你所需要的功能。 我们可以使用元类创建单例模式和实现 ORM 模式。

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

这三个方法属于 Python 的反射机制里面的,hasattr 可以判断一个对象是否含有某个属性,getattr 可以充当 get 获取对象属性的作用。而 setattr 可以充当 person.name = "liming"的赋值操作。代码示例如下:

79.请列举你知道的 Python 的魔法方法及用途。
__init__:
#类的初始化方法。它获取任何传给构造器的参数(比如我们调用 x = SomeClass(10, ‘foo’) , __init__就会接到参数 10 和 ‘foo’ 。 __init__在 Python 的类定义中用的最多。
__new__:
#__new__是对象实例化时第一个调用的方法,它只取下 cls 参数,并把其他参数传给 __init__ 。 __new__很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候.

__del__:
#__new__和 __init__是对象的构造器, __del__是对象的销毁器。它并非实现了语句 del x (因此该语句不等同于 x.__del__())。而是定义了当对象被垃圾回收时的行为。 当对象需要在销毁时做一些处理的时候这个方法很有用,比如 socket 对象、文件对象。但是需要注意的是,当 Python 解释器退出但对象仍然存活的时候,__del__并不会 执行。 所以养成一个手工清理的好习惯是很重要的,比如及时关闭连接。
80.如何知道一个 Python 对象的类型?

可以通过 type 方法

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

Python 中的传参即不是传值也不是传地址,传的是对象的引用。

82.Python 中的元类(metaclass)使用举例
'''
元类一般用于创建类。在执行类定义时,解释器必须要知道这个类的正确的元类,如果此属性没有定义,它会向上查找父类中的__metaclass__属性。如果还没发现,就查找全局变量。
元类也是一个类,是一个type类。
type可以创建类,如果存在一个方法,返回一个type动态创建的类,在使用class定义类的时候,把这个方法赋值给metaclass,就可以实现动态的改变class的定义。
'''
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]
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(list(filter(lambda x: x % 2 == 1, a)))

其实现在不推荐使用 filter,map 等方法了,一般列表生成式就可以搞定了。

85.什么是猴子补丁?

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

86.在 Python 中是如何管理内存的?
  • 垃圾回收:Python 不像 C++,Java 等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对 Python 语言来讲,对象的类型和内存都是在运行时确定的。这也是为什么我们称 Python 语言为动态类型的原因(这里我们把动态类型可以简单的归结为对变量内存地址的分配是在运行时自动判断变量类型并对变量进行赋值)。

  • 引用计数:Python 采用了类似 Windows 内核对象一样的方式来对内存进行管理。每一个对象,都维护这一个对指向该对对象的引用的计数。当变量被绑定在一个对象上的时候,该变量的引用计数就是 1,(还有另外一些情况也会导致变量引用计数的增加),系统会自动维护这些标签,并定时扫描,当某标签的引用计数变为 0 的时候,该对就会被回收。

  • 内存池机制 Python 的内存机制以金字塔行,1、2 层主要有操作系统进行操作

    • 第 0 层是 C 中的 malloc,free 等内存分配和释放函数进行操作
    • 第 1 层和第 2 层是内存池,有 Python 的接口函数 PyMem_Malloc 函数实现,当对象小于 256K 时有该层直接分配内存
    • 第 3 层是最上层,也就是我们对 Python 对象的直接操作
  • 经由内存池登记的内存到最后还是会回收到内存池,并不会调用 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、付费专栏及课程。

余额充值