首页 > 技术文章 > python面试的100题(16)

Fiona-Y 2019-03-21 17:40 原文

Python高级

元类

42.Python中类方法、类实例方法、静态方法有何区别?

类方法: 是类对象的方法,在定义时需要在上方使用 @classmethod 进行装饰,形参为cls,表示类对象,类对象和实例对象都可调用

类实例方法: 是类实例化对象的方法,只有实例对象可以调用,形参为self,指代对象本身;

静态方法: 是一个任意函数,在其上方使用 @staticmethod 进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系

43.遍历一个object的所有属性,并print每一个属性名?

class Car:
    def __init__(self,name,loss): # loss [价格,油耗,公里数]
        self.name = name
        self.loss = loss
    
    def getName(self):
        return self.name
    
    def getPrice(self):
        # 获取汽车价格
        return self.loss[0]
    
    def getLoss(self):
        # 获取汽车损耗值
        return self.loss[1] * self.loss[2]

Bmw = Car("宝马",[60,9,500]) # 实例化一个宝马车对象
print(getattr(Bmw,"name")) # 使用getattr()传入对象名字,属性值。
print(dir(Bmw)) # 获Bmw所有的属性和方法

结果为:

宝马
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'getLoss', 'getName', 'getPrice', 'loss', 'name']

44.写一个类,并让它尽可能多的支持操作符?

class Array:
__list = []

def __init__(self):
print("constructor")

def __del__(self):
print("destruct")

def __str__(self):
return "this self-defined array class"

def __getitem__(self,key):
return self.__list[key]

def __len__(self):
return len(self.__list)

def Add(self,value):
self.__list.append(value)

def Remove(self,index):
del self.__list[index]

def DisplayItems(self):
print("show all items---")
for item in self.__list:
print(item)

45.介绍Cython,Pypy Cpython Numba各有什么缺点

PyPy

在选择CPython的简易替代语言时,PyPy无疑是最佳之选(如Quora就是由它编写而成)。由于与现有Python代码保持高度兼容性,PyPy也是默认程序运行时的一个很好选择。
PyPy使用了Just-in-Time(JIT)即时编译器,即动态编译器,与静态编译器(如gcc,java等)不同,它是利用程序运行的过程的数据进行优化,拥有同谷歌Chrome V8 JavaScript引擎相同的语言加速技术。本月初,最新版本PyPy 2.5即将发布,此版本会有一系列的性能改进,提供更全面的如NumPy的支持,用于加速Python性能的共享库。
Python 3.x必须由单独的PyPy3项目构建而成。不过,对于边缘语言特性 爱好者来说,尽管预期支持3.3,但实际此版本最多仅支持Python3.2.4及以下版本。

Cython

Cython是Python 的C语言扩展。准确说Cython是单独的一门语言,专门用来写在Python里面import用的扩展库。实际上Cython的语法基本上跟Python一致,而Cython有专门的编译器:先将 Cython代码转变成C(自动加入了一大堆的C-Python API),然后使用C编译器编译出最终的Python可调用的模块。不过Cython的缺点是,你并不能真正编写Python代码,这样一来,现有代码将不会完全自动转移成功。
也就是说,Cython在提速方面有很大优势,它是一个用来快速生成Python扩展模块(extention module)的工具。而在Cython,C里的类型,如int,float,long,char*等都会在必要的时候自动转成python对象,或者从python对象转成C类型,在转换失败时会抛出异常,这正是Cython最神奇的地方。另外,Cython对回调函数的支持也很好。总之,如果你有写python扩展模块的需求,那么Cython真的是一个很好的工具。

Numba

Numba综合了前两种方法,是Cython的竞争项目。同样的,numba把Python源码通过LLVMPy生成JIT后的.so文件来加速。不同点在于,Numba是以JIT为主的,加速对源码的侵入性较小。而Cython则重点在加速高性能Python模块的开发上,不依赖LLVMPy项目。此外numba还很不成熟,目前兼容性相当差。

46.请描述抽象类和接口类的区别和联系

 1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

2、抽象类要被子类继承,接口要被类实现。

3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现

4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。

6、抽象方法只能申明,不能实现。abstract void abc();不能写成abstract void abc(){}。

7、抽象类里可以没有抽象方法

8、如果一个类里有抽象方法,那么这个类只能是抽象类

9、抽象方法要被实现,所以不能是静态的,也不能是私有的。

10、接口可继承接口,并可多继承接口,但类只能单根继承

特别是对于公用的实现代码,抽象类有它的优点。抽象类能够保证实现的层次关系,避免代码重复。然而,即使在使用抽 象类的场合,也不要忽视通过接口定义行为模型的原则。
从实践的角度来看,如果依赖于抽象类来定义行为,往往导致过于复杂的继承关系,而通过接口定义行为能 够更有效地分离行为与实现,为代码的维护和修改带来方便。

参考地址:https://blog.csdn.net/wangwei249/article/details/70168436

47.Python中如何动态获取和设置对象的属性?

1. 访问对象的属性

    dir([obj]):
    调用这个方法将返回包含obj大多数属性名的列表(会有一些特殊的属性不包含在内)。obj的默认值是当前的模块对象。
    hasattr(obj, attr): 
    这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值。
    getattr(obj, attr): 
    调用这个方法将返回obj中名为attr值的属性的值,例如如果attr为’bar’,则返回obj.bar。
    setattr(obj, attr, val):
    调用这个方法将给obj的名为attr的值的属性赋值为val。例如如果attr为’bar’,则相当于obj.bar = val。

2. 访问对象的元数据

  2.0. 准备工作:确定对象的类型

  在types模块中定义了全部的Python内置类型,结合内置方法isinstance()就可以确定对象的具体类型了。

    isinstance(object, classinfo):
    检查object是不是classinfo中列举出的类型,返回布尔值。classinfo可以是一个具体的类型,也可以是多个类型的元组或列表。 

  2.1. 模块(module)

    __doc__: 文档字符串。如果模块没有文档,这个值是None。
    *__name__: 始终是定义时的模块名;即使你使用import .. as 为它取了别名,或是赋值给了另一个变量名。
    *__dict__: 包含了模块里可用的属性名-属性的字典;也就是可以使用模块名.属性名访问的对象。
    __file__: 包含了该模块的文件路径。需要注意的是内建的模块没有这个属性,访问它会抛出异常! 

1     import fnmatch as m
2     print m.__doc__.splitlines()[0] # Filename matching with shell patterns.
3     print m.__name__ # fnmatch
4     print m.__file__ # /usr/lib/python2.6/fnmatch.pyc
5     print m.__dict__.items()[0] # ('fnmatchcase', <function fnmatchcase="" at="" 0xb73deb54="">)</function>

2.2. 类(class)

    __doc__: 文档字符串。如果类没有文档,这个值是None。
    *__name__: 始终是定义时的类名。
    *__dict__: 包含了类里可用的属性名-属性的字典;也就是可以使用类名.属性名访问的对象。
    __module__: 包含该类的定义的模块名;需要注意,是字符串形式的模块名而不是模块对象。
    *__bases__: 直接父类对象的元组;但不包含继承树更上层的其他类,比如父类的父类。 

1     print Cat.__doc__ # None
2     print Cat.__name__ # Cat
3     print Cat.__module__ # __main__
4     print Cat.__bases__ # (<type ?object?="">,)
5     print Cat.__dict__ # {'__module__': '__main__', ...}</type>

2.3. 实例(instance)

实例是指类实例化以后的对象。

    *__dict__: 包含了可用的属性名-属性字典。
    *__class__: 该实例的类对象。对于类Cat,cat.__class__ == Cat 为 True。

1     print cat.__dict__
2     print cat.__class__
3     print cat.__class__ == Cat # True

2.4. 内建函数和方法(built-in functions and methods)

根据定义,内建的(built-in)模块是指使用C写的模块,可以通过sys模块的builtin_module_names字段查看都有哪些模块是内建的。这些模块中的函数和方法可以使用的属性比较少,不过一般也不需要在代码中查看它们的信息。

    __doc__: 函数或方法的文档。
    __name__: 函数或方法定义时的名字。
    __self__: 仅方法可用,如果是绑定的(bound),则指向调用该方法的类(如果是类方法)或实例(如果是实例方法),否则为None。
    *__module__: 函数或方法所在的模块名。 

2.5. 函数(function)

这里特指非内建的函数。注意,在类中使用def定义的是方法,方法与函数虽然有相似的行为,但它们是不同的概念。

    __doc__: 函数的文档;另外也可以用属性名func_doc。
    __name__: 函数定义时的函数名;另外也可以用属性名func_name。
    *__module__: 包含该函数定义的模块名;同样注意,是模块名而不是模块对象。
    *__dict__: 函数的可用属性;另外也可以用属性名func_dict。 
    不要忘了函数也是对象,可以使用函数.属性名访问属性(赋值时如果属性不存在将新增一个),或使用内置函数has/get/setattr()访问。不过,在函数中保存属性的意义并不大。
    func_defaults: 这个属性保存了函数的参数默认值元组;因为默认值总是靠后的参数才有,所以不使用字典的形式也是可以与参数对应上的。
    func_code: 这个属性指向一个该函数对应的code对象,code对象中定义了其他的一些特殊属性,将在下文中另外介绍。
    func_globals: 这个属性指向当前的全局命名空间而不是定义函数时的全局命名空间,用处不大,并且是只读的。
    *func_closure: 这个属性仅当函数是一个闭包时有效,指向一个保存了所引用到的外部函数的变量cell的元组,如果该函数不是一个内部函数,则始终为None。这个属性也是只读的。 

2.6. 方法(method)

方法虽然不是函数,但可以理解为在函数外面加了一层外壳;拿到方法里实际的函数以后,就可以使用2.5节的属性了。

    __doc__: 与函数相同。
    __name__: 与函数相同。
    *__module__: 与函数相同。
    im_func: 使用这个属性可以拿到方法里实际的函数对象的引用。另外如果是2.6以上的版本,还可以使用属性名__func__。
    im_self: 如果是绑定的(bound),则指向调用该方法的类(如果是类方法)或实例(如果是实例方法),否则为None。如果是2.6以上的版本,还可以使用属性名__self__。
    im_class: 实际调用该方法的类,或实际调用该方法的实例的类。注意不是方法的定义所在的类,如果有继承关系的话。

这里讨论的是一般的实例方法,另外还有两种特殊的方法分别是类方法(classmethod)和静态方法(staticmethod)。类方法还是方法,不过因为需要使用类名调用,所以他始终是绑定的;而静态方法可以看成是在类的命名空间里的函数(需要使用类名调用的函数),它只能使用函数的属性,不能使用方法的属性。
2.7. 生成器(generator)

生成器是调用一个生成器函数(generator function)返回的对象,多用于集合对象的迭代。

    __iter__: 仅仅是一个可迭代的标记。
    gi_code: 生成器对应的code对象。
    gi_frame: 生成器对应的frame对象。
    gi_running: 生成器函数是否在执行。生成器函数在yield以后、执行yield的下一行代码前处于frozen状态,此时这个属性的值为0。
    next|close|send|throw: 这是几个可调用的方法,并不包含元数据信息,如何使用可以查看生成器的相关文档。

2.8. 代码块(code)

代码块可以由类源代码、函数源代码或是一个简单的语句代码编译得到。这里我们只考虑它指代一个函数时的情况;2.5节中我们曾提到可以使用函数的func_code属性获取到它。code的属性全部是只读的。

    co_argcount: 普通参数的总数,不包括*参数和**参数。
    co_names: 所有的参数名(包括*参数和**参数)和局部变量名的元组。
    co_varnames: 所有的局部变量名的元组。
    co_filename: 源代码所在的文件名。
    co_flags:  这是一个数值,每一个二进制位都包含了特定信息。较关注的是0b100(0×4)和0b1000(0×8),如果co_flags & 0b100 != 0,说明使用了*args参数;如果co_flags & 0b1000 != 0,说明使用了**kwargs参数。另外,如果co_flags & 0b100000(0×20) != 0,则说明这是一个生成器函数(generator function)。

2.9. 栈帧(frame)

栈帧表示程序运行时函数调用栈中的某一帧。函数没有属性可以获取它,因为它在函数调用时才会产生,而生成器则是由函数调用返回的,所以有属性指向栈帧。想要获得某个函数相关的栈帧,则必须在调用这个函数且这个函数尚未返回时获取。你可以使用sys模块的_getframe()函数、或inspect模块的 currentframe()函数获取当前栈帧。这里列出来的属性全部是只读的。

    f_back: 调用栈的前一帧。
    f_code: 栈帧对应的code对象。
    f_locals: 用在当前栈帧时与内建函数locals()相同,但你可以先获取其他帧然后使用这个属性获取那个帧的locals()。
    f_globals: 用在当前栈帧时与内建函数globals()相同,但你可以先获取其他帧……。

2.10. 追踪(traceback)

追踪是在出现异常时用于回溯的对象,与栈帧相反。由于异常时才会构建,而异常未捕获时会一直向外层栈帧抛出,所以需要使用try才能见到这个对象。你可以使用sys模块的exc_info()函数获得它,这个函数返回一个元组,元素分别是异常类型、异常对象、追踪。traceback的属性全部是只读的。

    tb_next: 追踪的下一个追踪对象。
    tb_frame: 当前追踪对应的栈帧。
    tb_lineno: 当前追踪的行号。 

3. 使用inspect模块

inspect模块提供了一系列函数用于帮助使用自省。下面仅列出较常用的一些函数,想获得全部的函数资料可以查看inspect模块的文档。
3.1. 检查对象类型

    is{module|class|function|method|builtin}(obj): 
    检查对象是否为模块、类、函数、方法、内建函数或方法。
    isroutine(obj): 
    用于检查对象是否为函数、方法、内建函数或方法等等可调用类型。用这个方法会比多个is*()更方便,不过它的实现仍然是用了多个is*()。 

3.2. 获取对象信息

    getmembers(object[, predicate]):
    这个方法是dir()的扩展版,它会将dir()找到的名字对应的属性一并返回,形如[(name, value), ...]。另外,predicate是一个方法的引用,如果指定,则应当接受value作为参数并返回一个布尔值,如果为False,相应的属性将不会返回。使用is*作为第二个参数可以过滤出指定类型的属性。
    getmodule(object):
    还在为第2节中的__module__属性只返回字符串而遗憾吗?这个方法一定可以满足你,它返回object的定义所在的模块对象。
    get{file|sourcefile}(object):
    获取object的定义所在的模块的文件名|源代码文件名(如果没有则返回None)。用于内建的对象(内建模块、类、函数、方法)上时会抛出TypeError异常。
    get{source|sourcelines}(object):
    获取object的定义的源代码,以字符串|字符串列表返回。代码无法访问时会抛出IOError异常。只能用于module/class/function/method/code/frame/traceack对象。
    getargspec(func): 
    仅用于方法,获取方法声明的参数,返回元组,分别是(普通参数名的列表, *参数名, **参数名, 默认值元组)。如果没有值,将是空列表和3个None。如果是2.6以上版本,将返回一个命名元组(Named Tuple),即除了索引外还可以使用属性名访问元组中的元素。  

getargvalues(frame): 
    仅用于栈帧,获取栈帧中保存的该次函数调用的参数值,返回元组,分别是(普通参数名的列表, *参数名, **参数名, 帧的locals())。如果是2.6以上版本,将返回一个命名元组(Named Tuple),即除了索引外还可以使用属性名访问元组中的元素。

 getcallargs(func[, *args][, **kwds]): 
    返回使用args和kwds调用该方法时各参数对应的值的字典。这个方法仅在2.7版本中才有
    getmro(cls): 
    返回一个类型元组,查找类属性时按照这个元组中的顺序。如果是新式类,与cls.__mro__结果一样。但旧式类没有__mro__这个属性,直接使用这个属性会报异常,所以这个方法还是有它的价值的。

   currentframe(): 
    返回当前的栈帧对象。 

参考地址:https://blog.csdn.net/qq_24861509/article/details/51744333

 

推荐阅读