python高级面试题目(二)

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

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

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

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


- 类属性  当多个对象都需要使用到同一个信息(不需要有差异化)的时候, 可以声明为类属性, 所有的中国人的国家就可以声明类属性

- 实例属性 和创建的实例对象的个数有关系, 类对象不能调用实例属性, 将对象独有的数据存储在实例属性中

- 实例方法 实例对象调用的方法, 特点: 第一个参数指向了调用的对象, 在内存只有一份(存储在类对象中) , 通过第一个参数self来区别不同的对象, 在某个方法中需要访问实例属性/实例方法可以定义实例方法

- 类方法 类对象调用, 内存中只有一份,之所以实例对象能够调用是因为实例对象有\__class__属性, 类方法的第一个参数cls指向了类对象 当需要访问到类属性/类方法的时候需要定义类方法

- 静态方法 和函数比较类似 实例对象和类对象都可以调用, 没有任何参数, 静态方法使用的并不多,  需要处理和该类相关的一些业务逻辑, 但是是用不到类属性/方法, 实例属性/方法 就可以定义静态方法

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

有时候我们会碰到这样的需求,需要执行对象的某个方法,或是需要对对象的某个字段赋值,而方法名或是字段名在编码代码时并不能确定,需要通过参数传递字符串的形式输入。举个具体的例子:当我们需要实现一个通用的DBM框架时,可能需要对数据对象的字段赋值,但我们无法预知用到这个框架的数据对象都有些什么字段,换言之,我们在写框架的时候需要通过某种机制访问未知的属性。

这个机制被称为反射。

# https://www.cnblogs.com/zh1164/p/6031464.html
class Cat():
    def __init__(self,name):
        self.name=name

    def sayHi(self):
        print('sayHi')

cat = Cat('kitty')

print(cat.name)

cat.sayHi()  # 调用实例方法

print(dir(cat))

if hasattr(cat, 'name'):  # 检查实例是否有这个属性
    setattr(cat, 'name', 'tiger')  # same as: a.name = 'tiger'
    print(getattr(cat, 'name') )
     # same as: print a.name

getattr(cat, 'sayHi')()

3.Python 的内存管理机制及调优手段?

内存管理机制:引用计数、垃圾回收、内存池。

引用计数:

引用计数是一种非常高效的内存管理手段, 当一个 Python 对象被引用时其引用计数增加 1, 当 其不再被一个变量引用时则计数减 1. 当引用计数等于 0 时对象被删除。

垃圾回收 :

  1. 引用计数 引用计数也是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集技术。当 Python 的某 个对象的引用计数降为 0 时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如 某个新建对象,它被分配给某个引用,对象的引用计数变为 1。如果引用被删除,对象的引用计数为 0, 那么该对象就可以被垃圾回收。不过如果出现循环引用的话,引用计数机制就不再起有效的作用了

  2. 标记清除 如果两个对象的引用计数都为 1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被 回收的,也就是说,它们的引用计数虽然表现为非 0,但实际上有效的引用计数为 0。所以先将循环引 用摘掉,就会得出这两个对象的有效计数。

  3. 分代回收
    从前面“标记-清除”这样的垃圾收集机制来看,这种垃圾收集机制所带来的额外操作实际上与系统 中总的内存块的数量是相关的,当需要回收的内存块越多时,垃圾检测带来的额外操作就越多,而垃圾 回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就将比垃圾回收带来更少的额 外操作。

举个例子:

当某些内存块 M 经过了 3 次垃圾收集的清洗之后还存活时,我们就将内存块 M 划到一个集合 A 中去,而新分配的内存都划分到集合 B 中去。当垃圾收集开始工作时,大多数情况都只对集合 B 进 行垃圾回收,而对集合 A 进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处 理的内存少了,效率自然就提高了。在这个过程中,集合 B 中的某些内存块由于存活时间长而会被转 移到集合 A 中,当然,集合 A 中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而 被延迟。

内存池:

1.Python 的内存机制呈现金字塔形状,-1,-2 层主要有操作系统进行操作;

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

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

4.第 3 层是最上层,也就是我们对 Python 对象的直接操作; Python 在运行期间会大量地执行 malloc 和 free 的操作,频繁地在用户态和核心态之间进行切 换,这将严重影响 Python 的执行效率。为了加速 Python 的执行效率,Python 引入了一个内存池 机制,用于管理对小块内存的申请和释放。Python 内部默认的小块内存与大块内存的分界点定在 256 个字节,当申请的内存小于 256 字节 时,PyObject_Malloc 会在内存池中申请内存;当申请的内存大于 256 字节时,PyObject_Malloc 的 行为将蜕化为 malloc 的行为。当然,通过修改 Python 源代码,我们可以改变这个默认值,从而改 变 Python 的默认内存管理行为。

调优手段(了解)
1.手动垃圾回收
2.调高垃圾回收阈值
3.避免循环引用(手动解循环引用和使用弱引用)

4.内存泄露是什么?如何避免?

指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。

内存泄漏并非指内存在物理上的 消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪 费。导致程序运行速度减慢甚至系统崩溃等严重后果。

del() 函数的对象间的循环引用是导致内存泄漏的主凶。 不使用一个对象时使用:del object 来删除一个对象的引用计数就可以有效防止内存泄漏问题。 通过 Python 扩展模块 gc 来查看不能回收的对象的详细信息。 可以通过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为 0 来判断是否内存 泄漏。

5. Python 函数调用的时候参数的传递方式是值传递还是引用传递?

https://blog.csdn.net/qq_35290785/article/details/92730973

函数的传值到底是值传递还是引用传递,要分情况:

不可变参数用值传递:

像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变 不可变对象

可变参数是引用传递的:

比如像列表,字典这样的对象是通过引用传递、和 C 语言里面的用指针传递数组很相似,可变对象 能在函数内部改变。

值传递还是引用传递

Python参数传递统一使用的是引用传递方式。因为Python对象分为可变对象(list,dict,set等)和不可变对象(number,string,tuple等),当传递的参数是可变对象的引用时,因为可变对象的值可以修改,因此可以通过修改参数值而修改原对象,这类似于C语言中的引用传递;当传递的参数是不可变对象的引用时,虽然传递的是引用,参数变量和原变量都指向同一内存地址,但是不可变对象无法修改,所以参数的重新赋值不会影响原对象,这类似于C语言中的值传递。
所以回到上面的问题引出,变量“a”,“b”,“val1”,“val2”其实都指向同一可变对象的内存地址,当通过变量“val2”对对象进行修改时,其他变量的值也相应被修改了。

6.对缺省参数的理解 ?

缺省参数指在调用函数的时候没有传入参数的情况下,调用默认的参数,在调用函数的同时赋值时, 所传入的参数会替代默认参数。

*args 是不定长参数,他可以表示输入参数是不确定的,可以是任意多个。

**kwargs 是关键字参数,赋值的时候是以键 = 值的方式,参数是可以任意多对在定义函数的时候 不确定会有多少参数会传入时,就可以使用两个参数。

7.为什么函数名字可以当做参数用?

Python 中一切皆对象,函数名是函数在内存中的空间,也是一个对象。

8.Python 中 pass 语句的作用是什么?

在编写代码时只写框架思路,具体实现还未编写就可以用 pass 进行占位,使程序不报错,不会进 行任何操作。

9.有这样一段代码,print c 会输出什么,为什么?

对于字符串、数字,传递是相应的值

a = 10
b = 20
c = [a]
a = 15
print(c)
#10

10.递归函数停止的条件?

递归的终止条件一般定义在递归函数内部,在递归调用前要做一个条件判断,根据判断的结果选择 是继续调用自身,还是 return;返回终止递归。 终止的条件: 1. 判断递归的次数是否达到某一限定值 2. 判断运算的结果是否达到某个范围等,根据设计的目的来选择

11.回调函数,如何通信的?

回调函数是把函数的指针(地址)作为参数传递给另一个函数,将整个函数当作一个对象,赋值给调 用的函数。

12.Python 主要的内置数据类型都有哪些?print dir( ‘a ’) 的输出?

内建类型:布尔类型、数字、字符串、列表、元组、字典、集合;

输出字符串“a”的内建方法;

# 举例
a=10
print(dir(a))
# ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

print(list(map(lambda x: x * x, [y for y in range(3)])))
# [0, 1, 4]

13. Python的hasattr() getattr() setattr() 函数使用方法详解

https://www.cnblogs.com/cenyu/p/5713686.html

hasattr(object, name)
判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。
需要注意的是name要用括号括起来

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

setattr(object, name, values)
给对象的属性赋值,若属性不存在,先创建再赋值。

一种综合的用法是:判断一个对象的属性是否存在,若不存在就添加该属性。

getattr(t, "age", setattr(t, "age", "18")) #age属性不存在时,设置该属性

14.什么是 lambda 函数? 有什么好处?

https://blog.csdn.net/Little_Fire/article/details/80499913

Lambda 函数是一个可以接收任意多个参数(包括可选参数)并且返回单个表达式值的函数

1、lambda 函数比较轻便,即用即仍,很适合需要完成一项功能,但是此功能只在此一处使用, 连名字都很随意的情况下;

2、匿名函数,一般用来给 filter, map 这样的函数式编程服务;

3、作为回调函数,传递给某些应用,比如消息处理

15.下面这段代码的输出结果是什么?请解释。

https://www.cnblogs.com/bigc008/p/10066795.html
def extendList(val, list=[]):
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print ("list1 = %s" % list1)
print ("list2 = %s" % list2)
print ("list3 = %s" % list3)

# list1 = [10, 'a']
# list2 = [123]
# list3 = [10, 'a']
def multipliers():
  return [lambda x : i * x for i in range(4)]

print ([m(2) for m in multipliers()])

# [6, 6, 6, 6]

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass

print (Parent.x, Child1.x, Child2.x)
Child1.x = 2
print (Parent.x, Child1.x, Child2.x)

Parent.x = 3
print (Parent.x, Child1.x, Child2.x)
# 1 1 1
# 1 2 1
# 3 2 3
def div1(x,y):
    print ("%s/%s = %s" % (x, y, x/y))

def div2(x,y):
    print ("%s//%s = %s" % (x, y, x//y))

div1(5,2)
div1(5.,2)
div2(5,2)
div2(5.,2.)

# 5/2 = 2.5
# 5.0/2 = 2.5
# 5//2 = 2
# 5.0//2.0 = 2.0
list = ['a', 'b', 'c', 'd', 'e']
print (list[10:])
#   []
list = [ [ ] ] * 5
print(list)

list[0].append(10)
print(list)

list[1].append(20)
print(list)

list.append(30)
print(list)
# 
# [[], [], [], [], []]
# [[10], [10], [10], [10], [10]]
# [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
# [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]

16.python请手写一个单例

https://www.jianshu.com/p/6a1690f0dd00

https://www.cnblogs.com/-qing-/p/10898623.html
image.png
'''类比电脑回收站,整个系统都用这个唯一回收站,回收站也自行提供自己的实例.'''

'''主要特点:确保某一个类只有一个实例对象,而且自行实例化并向整个系统提供这个实例,
这个类成为单例类,单例模式是一种对象创建型模式.'''


'''1.保证只有一个对象'''
'''2.只执行一次__init__方法'''

class Carfactory(object):
    '''定义一个类'''
    __first__new = True #确保是第一次 一旦创建对象就关闭
    __instance = None #默认没有实例对象
    __first__init = True

    def __new__(cls,*args,**kwargs):
        '''创建对象'''
        if cls.__first__new:
            cls.__first__new = False
            cls.__instance = object.__new__(cls)
            return cls.__instance
        else:
            return cls.__instance


    def __init__(self, new_name):
        '''初始化对象属性'''
        if Carfactory.__first__init:
            self.new_name = new_name
            Carfactory.__first__init = False

a = Carfactory('baoma')
b = Carfactory('aodi')
print(id(a))
print(id(b))
# id指向同一个对象

17.单例模式的应用场景有哪些?

单例模式应用的场景一般发现在以下条件下:

(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。

(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。

1.网站的计数器 2.应用配置 3.多线程池 4. 数据库配置,数据库连接池 5.应用程序的日志应用....

18. Python理解装饰器并写出一个计时器记录方法执行性能的装饰器?

装饰器本质上是一个callable object ,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。

import time
from functools import wraps

def timeit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.clock()
        ret = func(*args, **kwargs)
        end = time.clock()
        print('used:',end-start)
        return ret
    
    return wrapper
@timeit
def foo():
    print('in foo()'foo())

19.解释一下什么是闭包?

在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为 闭包。

20.函数装饰器有什么作用?

装饰器本质上是一个 Python 函数,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能。装 饰器的返回值也是一个函数的对象,它经常用于有切面需求的场景。 比如:插入日志、性能测试、事务处理、缓存、 权限的校验等场景 有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。

21.生成器、迭代器的区别?

https://www.cnblogs.com/hzyimen/p/12370451.html

迭代器是一个更抽象的概念,任何对象,如果它的类有 next 方法和 iter 方法返回自己本身,对于 string、list、
dict、tuple 等这类容器对象,使用 for 循环遍历是很方便的。在后台 for 语句对容器对象调用 iter()函数,iter()
是 python 的内置函数。iter()会返回一个定义了 next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是 python 的内置函数。在没有后续元素时,next()会抛出一个 StopIteration 异常。

生成器(Generator)是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在需要返回数
据的时候使用 yield 语句。每次 next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置
和所有的数据值)

区别:生成器能做到迭代器能做的所有事,而且因为自动创建了 iter()和 next()方法,生成器显得特别简洁,而且
生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当
发生器终结时,还会自动抛出 StopIteration 异常。

22.X = (for i in range(10)),X返回的是什么?

X 是 generator 类型

 at 0x7eff5ae72ca8>

23.请尝试用“一行代码”实现将 1-N 的整数列表以 3 为单位分组,比如 1-100 分组后为?

https://blog.csdn.net/qq_40808154/article/details/94500640
print([[x for x in range(1,101)][i:i+3] for i in range(0,100,3)])

21.Python 中 yield 的用法?

https://blog.csdn.net/mieleizhi0522/article/details/82142856/

22.Python 中的可变对象和不可变对象?

不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。
Python 中,数值类型(int 和 float)、字符串 str、元组 tuple 都是不可变类型。而列表 list、字典 dict、集合 set 是可变类型。

23.Python 中 is 和==的区别?

is 判断的是 a 对象是否就是 b 对象,是通过 id 来判断的。
==判断的是 a 对象的值是否和 b 对象的值相等,是通过 value 来判断的。

24.Python 的魔法方法?

new才是实例化对象调用的第一个方法,它只取下 cls 参数,并把 其他参数传给 init

new很 少使用,但是也有它适合的场景,尤其 是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。

call 允许一个类的实例像函数一样被调用 。

getitem 定义获取容器中指定元素的行为,相当于 self[key] 。

getattr 定义当用户试图访问一个不存在属性的时候的行为 。

setattr 定义当一个属性被设置的时候的行为 。

getattribute 定义当一个属性被访问的时候的行为 。

25.谈谈你对面向对象的理解?

面向对象是相对于面向过程而言的。面向过程语言是一种基于功能分析的、以算法为中心的程序设计方 法;而面向对象是一种基于结构分析的、以数据为中心的程序设计思想。在面向对象语言中有一个有很重要 东西,叫做类。面向对象有三大特性:封装、继承、多态。

26.面向对象中怎么实现只读属性?

https://www.jianshu.com/p/dd0e1487a4d6

27.Python 里 match 与 search 的区别?

match()函数只检测 RE 是不是在 string 的开始位置匹配, search()会扫描整个 string 查找匹配;

也就是说 match()只有在 0 位置匹配成功的话才有返回, 如果不是开始位置匹配成功的话,match()就返回 none。

28.Python 字符串查找和替换?

re.findall(r’目的字符串’,’原有字符串’) #查询
re.findall(r'cast','itcast.cn')[0]
re.sub(r‘要替换原字符’,’要替换新字符’,’原始字符串’)

29.用 Python 匹配 HTML g tag 的时候,<.> 和 <.?> 有什么区别?

<.>是贪婪匹配,会从第一个“<”开始匹配,直到最后一个“>”中间所有的字符都会匹配到,中间可能会包含 “<>”。 <.?>是非贪婪匹配,从第一个“<”开始往后,遇到第一个“>”结束匹配,这中间的字符串都会匹配到,但是 不会有“<>”。

30.请写出下列正则关键字的含义?

省略

你可能感兴趣的:(python高级面试题目(二))