python 高级

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

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

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

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

2. 内存管理与垃圾回收机制

  1. Python的内存管理机制及调优手段
    内存管理机制:引用计数,垃圾回收,内存池

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

垃圾回收:
1. 引用计数
引用计数也是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集计数。当python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。不过出现循环引用的话,引用计数机制就不再起有效的作用了。
2. 标记清除
如果两个对象的引用计数都为1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的,也就是说,它们的引用计数虽然表现为非0, 但实际上有效的引用计数为0。所以先将循环引用摘掉,就会得出这两个对象的有效计数。
3. 分代回收
从前面 "标记-清除" 这样的垃圾收集机制来看,这种垃圾收集机制所带来的额外操作实际上与系统中种的内存块的数据量是相关的,当需要回收的内存块越多时,垃圾检测带来的额外操作就越多,而垃圾回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就将比垃圾回收带来更少的额外操作。

内存池:

  1. Python的内存机制呈现金字塔形状, -1 ,-2 层主要由操作系统进行操作。
  2. 第0层是C中的malloc, free 等内存分配和释放函数进行操作。
  3. 第一层和第二层是内存池,有Python的接口函数 PyMem_Malloc 函数实现,当对象小于 256K时 该层直接分配内存。
  4. 第三层是最上层,也就是我们对Python对象的直接操作;
    Python 在运行期间会大量地执行malloc 和 free 的操作,频繁的在用户态和核心态之间进行切换,这将严重影响Python的执行效率。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
    Python 内部默认的小块内存和大块内存的分界点定在 256 个字节,当申请的内存小于 256 字节时,Pyobject_malloc 会在内存池中申请内存,当申请的内存大于256字节时,Pyobject_malloc的行为将蜕化为malloc的行为。当然,通过修改Python源代码,我们可以改变这个默认值,从而改变Python的默认内存管理。

调优手段(了解)

  1. 手动垃圾回收
  2. 调高垃圾回收阈值
  3. 避免循环引用

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

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

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

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

5. Python主要的内置数据类型都有哪些?

内建类型: 布尔类型,数字,字符串,列表, 元祖, 字典, 集合

6. hasattr(), getattr(), setattr() 函数使用详解?

hasattr(object, name) 函数:
判断一个对象里面是否有name属性或者name方法,返回bool值,有name属性返回TRUE,否则返回FALSE
getattr(object, name [,default])函数:
获取对象 object 的属性和方法,如果存在则打印出来,如果不存在,打印默认值,默认值可选。
setattr(object, name, values) 函数:
给对象的属性赋值,若属性不存在,先创建再赋值

7. 什么事lambda函数,有什么好处?

lambda 函数式匿名函数,使用lambda函数能创建小型匿名函数。这种函数等于省略了声明函数的标准步骤;
lambda 函数是一个可以接收任意多个参数并且返回的那个表达式值得函数

  1. lambda 函数比较轻便,即用即扔, 很适合需要完成一项功能,但是此功能只在此一处使用,连名字都很随意的情况下。
  2. 匿名函数,一般用来给filter, map这样的函数式编程服务。
  3. 作为回调函数,传递给某些应用,比如消息处理

8. 请手写一个单例

点击查看代码
class A(object):
  __instance = None
  def __new__(cls, *args, **kwargs):
    if cls.__instance is None:
        cls.__instance = object.__new__(cls)
        return cls.__instance
    else:
        return cls.__instance

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

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

  1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。比如日志文件,应用配置
  2. 控制资源的情况下,方便资源之间的互相通信。如线程池等。
    1. 网站的计数器
    2. 应用配置
    3. 多线数据库配置
    4. 数据库连接池
    5. 应用程序的日志应用

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

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

点击查看代码
import time
def timeit(func):
  def wrapper():
    start = time.clock()
    func()
    end = time.clock()
    print('use_time: 'end-start) 
  return wrapper
@timeit
def foo():
  print('in foo()')
foo()

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

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

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

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

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

不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后在改变,这会开辟一个新的地址,变量再指向这个新的地址。
可变对象,该对象所指向的内存中的值可以被改变。变量改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟出新的地址。
Python 中,数值类型, 字符串,元祖都是不可变类型。而列表,字典 ,集合 都是可变类型。

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

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

15. Python的魔法方法?

魔法方法就是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被Python所调用,你可以定义自己想要的行为,而这一切都是自动发生的。他们经常是两个下划线包围来命名的,Python的魔法方法非常强大。
init 构造器,当一个实例被创建的时候初始化的方法。但是它并不是实例化调用的第一个方法。
__new__才是实例化对象调用的第一个方法,他只取下 cls 参数,并把其他参数传给 init。__new__很少使用,但是也有它适合的场景,尤其是当类继承自一个元祖或字符串这样不经常改变的类型的时候。
call 允许一个类的实例像函数一样被调用。
getitem 定义获取容器中指定元素的行为,相当于self[key]
getattr 定义当用户试图方位一个不存在属性的时候的行为。
setattr 定义当一个属性被设置的时候的行为。
getattribute 定义当一个属性被访问的时候的行为。

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

将对象私有化,通过共有方法提供一个读取数据的接口。

点击查看私有化案例
class Person():
  def __init__(self, x):
    self.__age = 10
  def age(self):
    return self.__age
 t = Person(22)
 print(t.age())

最好的方法
class MyCls(object):
  __weight = 50
  @property   # 以访问属性的方式来访问weight 方法
  def weight(self):
    return self.__weight
if __name__ == '__main__':
  obj = MyCls()
  print(obj.weight)
  obj.weight = 12
可以试一下 看看结果
点击查看结构
50
File "C:/PythonTest/test.py", line 11, in <module> 
obj.weight = 12 
AttributeError: can't set attribute
## 17. 谈谈你对面向对象的理解? 面向对象是相当于面向过程而言的。面向过程语言是一种基于功能分析的,以算法为中心的程序设计方法,而面向对象是一种基于结构分析的,以数据为中心的程序设计思想。在面向对象语言中有一个很重要的东西,叫做类。 面向对象有三大特性: 封装,继承,多态。
posted @ 2022-03-20 22:11  恰恰的故事  阅读(82)  评论(0编辑  收藏  举报