1、解释型语言与编译性语言是什么意思?
编译型语言:是指在运行程序之前,需要通过编译器将源代码翻译成可执行代码,然后再执行的语言。在编译期间,编译器会对代码进行各种优化,使得程序在运行时效率更高。编译型语言通常需要经过较长的开发周期,但编写出来的程序能够在短时间内高效运行。代表语言有C、C++、Golang、汇编等。
解释型语言:是指在运行程序时逐行翻译源代码,将其转换为机器语言并执行的语言。在解释型语言中,源代码不需要预先编译,不会生成可执行程序,可以直接运行。解释型语言的特点是更容易阅读和调试,开发周期较短,但相对于编译型语言,程序的运行时效率通常较低。代表语言有JavaScript、Python、PHP、Shell等。
2、面向对象和面向过程分别是什么?
面向过程:是一种以过程为中心,按照一系列操作的顺序进行编程的方法。在面向过程编程中,通过将问题划分为一系列的步骤,然后编写完成这些步骤的函数或过程来解决问题。面向过程编程强调算法和数据的处理,它将程序看作是一系列的操作步骤,更加注重解决问题的步骤和流程,适合于简单、小型的程序开发。
面向对象:是一种以对象为基础,通过封装、继承和多态等概念,将程序划分为多个相互交互的对象来进行编程的方法。在面向对象编程中,将问题抽象成一个个对象,每个对象具有特定的属性和行为,并且可以根据需要定义类,从类实例化出对象。面向对象编程强调数据和操作的封装性,提高了代码的可重用性、可维护性和可扩展性,适合于大型项目的开发。
3、Python优点有哪些?
① 简洁优雅:Python 采用简洁的语法和清晰的代码风格,使得代码易读易写,减少了开发人员的编码时间和工作量。
② 容易学习:Python 的语法简单易懂,适合初学者入门。它具有较少的关键字和特殊字符,使得编程门槛较低。
③ 跨平台性:Python 可以在多个操作系统上运行,包括 Windows、Linux、Mac OS 等,保证了代码的可移植性。
④ 强大的标准库:Python 提供了丰富的标准库和第三方库,包含了各种常用功能和工具,可以方便地进行开发和扩展。
⑤ 大量的资源和社区支持:Python 拥有庞大的用户社区,开发者可以获得丰富的文档、教程和支持资源。
⑥ 可扩展性:Python 可以轻松与其他语言(如C/C++)进行集成,通过调用外部函数和库来实现更高性能的计算。
4、Python缺点有哪些?
① 运行速度相对较慢:与一些编译型语言相比,Python 的解释执行速度较慢,特别是在处理大规模计算和高性能需求时。
② 全局解释器锁(GIL):在 CPython 解释器(CPython 是 Python 的默认解释器,也是最常用的解释器之一。它是使用 C 语言实现的,因此得名 “CPython”。)中,由于全局解释器锁的存在,多线程程序的并发性能有限。但可以通过使用多进程或其他解释器来规避这个问题。
③ 设计限制:Python 的设计中有一些限制,如不能直接访问底层内存和硬件,这在一些特定的应用场景下可能会受到影响。
5、Python的语言特性是什么?
① 动态类型:Python 是一种动态类型语言,变量的类型在运行时可以动态变化,开发过程中更加灵活。
② 面向对象:Python 支持面向对象编程,提供类、继承、封装和多态等面向对象的特性。
③ 垃圾回收:Python 自带垃圾回收机制,开发者不需要手动管理内存,减少了内存泄漏的风险。
④ 异常处理:Python 提供了方便的异常处理机制,可捕获和处理程序运行中的异常情况。
⑤ 可读性强:Python 代码简洁易读,具有良好的可读性,降低了代码的维护成本。
6、什么是装饰器(decorator)?
装饰器(Decorator)是一种特殊的语法,用于修改或增强函数或类的功能。装饰器本质上是一个函数(或使用类实现的可调用对象),它接收一个函数作为输入,并返回一个新的函数或可调用对象。
装饰器提供了一种简洁而灵活的方式来修改函数或类的行为,而不需要直接修改它们的源代码。通过将装饰器应用到目标函数或类上,我们可以在执行目标函数或类之前、之后或周围执行额外的逻辑(比如:插入日志、性能测试、事务处理、缓存、权限校验等)。
装饰器通常使用@符号和装饰器函数的名称来应用于目标函数或类。当定义一个带有装饰器的函数时,装饰器会在函数定义后立即运行,并将被修饰的函数作为自己的输入参数。装饰器可以在不修改原函数代码的情况下,修改其行为或添加功能。
示例:
import time
def calc_time(func):
def inner():
t1 = time.time()
func()
t2 = time.time()
print('cost time: {}s'.format(t2-t1))
return inner
@calc_time
def test(x):
for i in range(1000):
print(x,end='')
print('\n')
return x
test(1)
7、Python的装饰器@staticmethod和@classmethod有什么区别?要如何使用?
使用:使用@staticmethod或@classmethod,只需将其放置在要修饰的方法上方即可。
使用修饰器时可以不需要实例化,直接类名.方法名()来调用。这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
区别:类的普通方法,第一个参数需要self参数表示自身。
@staticmethod:将一个方法声明为静态方法。静态方法和类无关,不会接收隐式的第一个参数(通常是实例对象或类对象)self,和自身类的cls参数,可以通过类名直接调用,也可以通过实例对象调用。静态方法内部无法访问类属性或实例属性。
@classmethod:将一个方法声明为类方法。类方法会被绑定到类上,第一个参数通常是 cls(表示类本身),可以通过类名直接调用,也可以通过实例对象调用。类方法可以访问和修改类属性,但不能访问实例属性。
示例:
class Test(object):
"""docstring for Test"""
def __init__(self, arg=None):
super(Test, self).__init__()
self.arg = arg
def say_hi(self):
print 'hello wrold'
@staticmethod
def say_bad():
print 'say bad'
@classmethod
def say_good(cls):
print 'say good'
def main():
test = Test()
test.say_hi()
Test.say_bad() //直接类名.方法名()来调用
Test.say_good() //直接类名.方法名()来调用
if __name__ == '__main__':
main()
8、深拷贝、浅拷贝和等号赋值的区别。
① 等号赋值:相当于为原来的对象打一个新的标签,两个引用指向同一个对象,修改其中的一个,另一个也会产生变化。
② 深拷贝:新建一个对象,把原来对象的内存完全复制过来,改变复制后的对象,不会改动原来内存的内容。(两个对象在复制之后是完全独立的对象)。
③ 浅拷贝:存在两种情况,
a. 浅拷贝的值是不可变对象(数值、字符、元组)时,和等号赋值一样,对象的id值和浅拷贝原来的值相同;
b. 如果是可变对象(列表、字典等),
- 一个简单的没有嵌套的对象,复制前后的对象相互之间不会影响,
- 对象中有复杂子对象,如列表嵌套,如果改变原来的对象中复杂子对象的值,浅拷贝的值也会受影响,因为在浅拷贝时只复制了子对象的引用(只拷贝父对象)。
9、在Python中如何实现多线程?
在Python中可以使用threading模块来实现多线程,但是Python并不支持真正意义上的多线程。全局解释器锁(Global Interpreter Lock,简称GIL)是Python解释器中的一个机制,用于控制同一时间只有一个线程执行 Python 字节码。尽管使用多线程可以实现并发执行,但实际上只能利用到单个 CPU 核心的计算能力,无法充分发挥多核处理器的优势。当应用程序需要大量的CPU密集型运算时,GIL会导致多线程无法真正并行执行,从而限制了性能的提升。然而,对于I/O密集型的任务,GIL的影响相对较小,因为线程通常会在进行I/O操作时释放GIL,允许其他线程执行。
10、GIL是什么?
全局解释器锁(Global Interpreter Lock,简称GIL)是Python解释器中的一个机制,用于控制同一时间只有一个线程执行 Python 字节码。能确保一次执行一个线程,线程轮流保存GIL并且在把他传给下一个线程之前执行一些操作,以达到多个进程在CPU上轮流运行,但是这个转换速度很快,让我们觉得他是并行的。
11、Python如何进行内存管理的?
Python使用自动化的内存管理机制,称为垃圾回收(Garbage Collection),来管理内存的分配和释放。它通过追踪对象的引用计数和使用循环引用检测来管理内存。
① 引用计数:Python使用引用计数来跟踪对象的引用情况。每当一个对象被引用时,它的引用计数加1,当它不再被引用时,计数减1。当引用计数为0时,对象将被销毁并释放其占用的内存。
② 循环引用检测:Python的垃圾回收机制还能够检测并处理循环引用的情况。当两个或多个对象之间存在循环引用时,即使它们的引用计数不为0,垃圾回收器也会将其识别为垃圾对象,并清理掉它们占用的内存空间。
③ 分代回收:Python的垃圾回收器使用分代回收策略。它将对象分为不同的代(Generation),根据对象的生命周期来选择性地进行回收。通常情况下,大部分对象都是在新生代(Generation 0)创建并销毁的,只有持续存活的对象才会被推入到后续的代中。
④ 引用循环的手动处理:尽管Python的垃圾回收机制可以自动处理大多数引用循环情况,但在一些特殊情况下,如使用了带有 、__del__ 方法的对象、全局变量、循环数据结构等,可能需要手动断开循环引用,通过显式地将引用置为 None 或使用其他方法来释放内存。
PS:内存池机制:在Python中,许多时候申请的内存都是小块的内存,这些小块内存在申请后,很快又会被释放,由于这些内存的申请并不是为了创建对象,所以并没有对象一级的内存池机制。这就意味着Python在运行期间会大量地执行malloc和free的操作,频繁地在用户态和核心态之间进行切换,这将严重影响Python的执行效率。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
- Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
- Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的 malloc。另外Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
12、谈一谈Python的垃圾回收机制。
Python的垃圾回收机制是一种自动内存管理的机制,用于回收不再使用的对象所占据的内存空间,没有内存泄漏的隐患。Python使用了以引用计数机制为主,标记-清除和分代回收两种机制为辅。
① 计数机制:每个对象都有一个引用计数器,用于统计当前有多少个引用指向该对象。当引用计数器归零时,表示没有任何引用指向该对象,即该对象不再被使用,可以被回收。
优点是回收迅速,只需要在引用发生改变时增减计数器;缺点是存在循环引用的问题,如果l1和l2相互引用,没有其他的对象引用他们,这两段内存永远无法被回收。
② 标记-清除:是在循环垃圾回收过程中使用的一种核心算法。这个算法通过遍历对象图,从根对象出发,标记所有可以访问到的对象。然后,在清除阶段,会扫描内存,清除未被标记的对象,并回收它们所占据的内存空间。
③ 分代回收:Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
13、*args和**kwargs这两个参数是什么意思?
*args,**kwargs并不是必须这样设定的,只是一个约定俗成的名字,*args(任意多个位置参数),**kwargs(任意多个关键字参数)。都用于函数的定义,用于将可变参数(不定数量的参数)传递给函数。
① 在函数内部,*args被视为一个包含所有位置参数的元组(tuple)。使用*args时,你可以把多个参数进行传递,而不需要知道使用者会传递多少参数。
② 在函数内部,**kwargs被视为一个包含所有关键字参数的字典(dict)。使用**kwargs时,你可以向函数传递任何数量的关键字参数,这些参数将会被打包成一个字典。
*args和**kwargs通常只放在参数列表的最后,而且在同时使用*args和**kwargs时,*args参数必须要列在**kwargs前,否则会报错。
示例:
def test(a,*args,**kwargs):
print(a)
print(args)
print(kwargs)
test(1,3,5,7,c='2',d=4)
输出为:
1
(3, 5, 7)
{‘c’: ‘2’, ‘d’: 4}
14、join()和split()有什么区别?
join()方法:是将一个可迭代对象(通常是一个字符串列表)中的所有元素按照指定的分隔符连接起来,并返回一个新的字符串。
split()方法:是将一个字符串按照指定的分隔符分割成多个子串,并返回一个列表。
15、strip(), lstrip(), rstrip()有什么不同?
strip()、lstrip()和rstrip()是用于移除字符串首尾的空白字符(如空格、制表符、换行符等)的方法。
① strip()方法用于移除字符串两端的空白字符。如果没有提供参数,默认会移除字符串两端的所有空白字符。
② lstrip()方法用于移除字符串左边(开头)的空白字符。如果没有提供参数,默认会移除字符串左边的所有空白字符。
③ rstrip()方法用于移除字符串右边(结尾)的空白字符。如果没有提供参数,默认会移除字符串右边的所有空白字符。
这些方法返回的是处理后的新字符串,原始字符串并不会改变。除了不带参数的用法外,这些方法还可以接受一个参数,指定要移除的字符集合。例如,strip(‘!’)会移除字符串两端的感叹号字符。
16、is开头的函数有哪些作用?
① 用于检查一个对象是否属于指定类型或其子类。
例如:isinstance(),issubclass()
② 判断字符串的特定属性或类型。
例如:islower(), isupper(), istitle(),isalnum(), isdigit(), isnumeric(), isdecimal()
17、pass有什么作用?
pass是一个空语句,它不执行任何操作。pass通常用作占位符,在需要有语法结构但又不需要执行任何代码的情况下使用。
pass的主要作用是保持语法完整性,因为在Python中,某些语法结构(如函数、类、条件语句等)必须包含至少一条语句。如果你在这些结构中没有实际的代码要执行,但又需要语法正确,你可以使用pass来填充这些位置。
以下是几种常见的使用场景:
① 函数或类的定义:当你正在编写一个函数或类的框架时,但是函数体或类的实现部分尚未完成,你可以使用pass作为占位符。
示例:
def my_function():
pass # TODO: Add function implementation
class MyClass:
def __init__(self):
pass # TODO: Add class initialization
② 条件语句的占位:当你希望先编写条件语句的框架,但是具体的条件判断或执行逻辑还没有确定时,可以使用pass作为占位符。
示例:
if condition:
pass # TODO: Add condition handling
else:
pass # TODO: Add alternative handling
③ 空的循环体:有时候你可能需要创建一个空的循环体,例如当你正在设计一个基于条件的迭代算法时。你可以使用pass来表示循环体内部尚未实现任何具体逻辑。
示例:
while condition:
pass # TODO: Add loop iteration steps
18、yield有什么作用?
yield 是 Python 中的一个关键字,用于定义生成器函数。生成器函数是一种特殊的函数,它可以在函数执行期间多次产生值,并且每次产生一个值后暂停函数的执行,等待下一次调用时再继续执行。这些生成器函数可以通过迭代器协议进行迭代,从而逐个产生值。
简单地说,就是能保存当前运行状态(断点),然后暂停执行,将函数挂起。将yeild关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用,当使用next()、send()函数让函数从断点处继续执行,即唤醒函数。
19、match()和search()都是用于字符串匹配的函数,区别是什么?
① match()函数:
match()函数从字符串的起始位置开始匹配模式。如果模式与字符串的开头完全匹配,则返回一个匹配对象;否则返回None。
match()函数只会尝试一次匹配,并且只匹配字符串的开头部分。如果你需要从字符串的任意位置开始匹配,应该使用search()函数。
② search()函数:
search()函数在整个字符串中搜索并找到第一个匹配模式的子串。如果找到匹配,则返回一个匹配对象;否则返回None。
search()函数会从字符串的任意位置开始进行搜索,而不仅仅是在开头部分。
③ 正则表达式模式:
match()和search()函数都可以使用正则表达式作为模式进行匹配。
但值得注意的是,match()函数必须以模式从字符串的开头进行匹配,而search()函数可以在字符串中的任何位置找到匹配。
20、说说模块和包。
模块(Module):是一个包含 Python 定义和语句的文件。它可以包含函数、类、变量和常量等代码。你可以将一个模块看作是一个独立的程序文件,它可以被其他程序导入和使用。模块使得代码的组织和复用更加方便,也便于维护和测试。
要使用模块,需先导入它。导入模块可以使用 import 关键字,导入指定模块或模块中的特定内容。
包(Package):是一个包含多个模块的目录。它通过一个特殊的 init.py 文件来标识该目录为一个包。包的主要目的是为了更好地组织和管理相关的模块。通过使用包,可以按逻辑关系将模块进行分类和分组,使得项目结构更加清晰。库:具有相关功能模块的集合,python有很多标准库、第三方库。
闭包(Closure):是指函数对象可以访问其自由变量(在函数定义时不在本地作用域内定义的变量)的能力。简单来说,闭包是一个函数以及其在定义时可以访问的外部变量的组合。闭包使得局部变量在函数外被访问成为可能。
21、Python运算符有哪些?
有以下7种,算术、关系、赋值、逻辑、位、成员、身份运算符。
22、如何表示多进制数字?
① 二进制(Binary):以0b或0B开头,后面跟着由0和1组成的数字序列。例如,0b1010表示十进制数10。
② 八进制(Octal):以0o或0O开头,后面跟着由0到7组成的数字序列。例如,0o23表示十进制数19。
③ 十六进制(Hexadecimal):以0x或0X开头,后面跟着由0到9和A到F(不区分大小写)组成的数字序列。例如,0x1A表示十进制数26。
还可以使用bin()、oct()和hex()这三个内置函数将十进制数转换为对应的二进制、八进制和十六进制字符串表示形式。
23、Python有多少种标准数据类型?
有如下8种,
不可变数据:
number (数字类型)
string (字符串类型)
tuple (元组类型)
boolean(布尔值)
none(空值)
可变数据:
set (集合类型)
dictionary(字典类型)(key、value)
list (列表类型)
24、tuple(元组)是如何表示的?
例如:tuple1=(“hello”,2,True)
tuple 是使用( )小括号包含各个数据项。
tuple 与 list 的唯一区别是 tuple 的元素是不能修改,而 list 的元素可以修改。
25、set(集合)是如何表示的?
例如:set1={1,2,3,“nihao”,“hello”,1,2}
set2=set(“hello word”)
set 是一个无序不重复元素的序列,使用大括号 { } 或者 set() 函数创建集合。
用 set() 创建一个空集合。使用 set 可以去重。
26、PYTHONPATH变量是什么?
Python中的环境变量,用于在导入模块的时候搜索路径。因此它必须包含Python源库目录以及含有Python源代码的目录。
27、生成器generator和迭代器iterator有什么区别和联系?
当我们需要处理大量数据或者延迟计算时,生成器(generator)和迭代器(iterator)是Python中非常有用的工具。它们提供了一种高效且内存友好的方式来遍历序列或者生成元素。
① 迭代器(Iterator)是一个对象,它实现了迭代协议,即具有__iter__()和__next__()方法。通过调用iter(iterable)函数,我们可以获取一个迭代器对象。迭代器每次返回一个元素,并通过StopIteration异常来标志迭代结束。迭代器只能往前迭代,无法回退或重复遍历。
② 生成器(Generator)是一种特殊的迭代器,它可以通过函数来创建。使用生成器的关键字是yield,它用于返回一个值,并暂停函数的执行状态。每次调用生成器的next()方法时,函数将从上次暂停的位置继续执行,直到再次遇到yield语句。
联系:
- 迭代器和生成器都支持按需生成数据,而不需要一次性存储所有的数据,从而节省内存空间。
- 迭代器和生成器都可以用于遍历序列或者处理大量数据。
- 生成器是一种特殊的迭代器,可以通过函数来创建。因此,生成器自动实现了迭代器的协议,即具有__iter__()和__next__()方法。
区别: - 创建方式:迭代器可以手动实现迭代器协议,需要定义__iter__()和__next__()方法;而生成器使用关键字yield在函数中定义,并通过yield语句返回值。
- 成器简单易用,通过yield语句来控制迭代状态和结束条件,简化了迭代器的实现。
- 状态保存:迭代器需要手动维护迭代状态和临时变量,以便在每次迭代时返回正确的值;而生成器会自动保存函数的执行状态,在每次调用生成器的next()方法时,函数会从上次暂停的位置继续执行,无需手动保存状态和变量。
- 迭代完毕:迭代器使用StopIteration异常来标志迭代结束;而生成器在函数体执行完毕后会自动抛出StopIteration异常,不需要显式处理。
综上所述,生成器是一种特殊的迭代器,通过函数和yield语句创建,提供了一种更简洁、更易用的方式来实现迭代。它们都可以按需生成数据,但生成器相对于迭代器更加简洁和方便。在大多数情况下,我们更倾向于使用生成器来处理迭代任务。
28、生成器和函数有什么区别?
① 返回值:函数使用 return 语句来返回值,并且一旦执行到 return 语句,函数的执行就会结束。生成器使用 yield 语句来产生值,并且可以在后续调用中从上次 yield 语句的位置继续执行。
② 状态保存:函数在每次调用结束后会失去其局部变量的值(除非使用全局变量或其他方式保存状态),而生成器可以在每次调用时保持自己的状态,并且记住上一次 yield 语句的位置。
③ 惰性求值:生成器是一种惰性求值(Lazy Evaluation)的机制,它的值是按需生成的。函数一般会立即执行并返回结果。
④ 迭代器:生成器是一种特殊的迭代器(Iterator),可以通过 for 循环逐个访问生成器产生的值。函数不一定是迭代器。
29、参数传递机制是什么?
在 Python 中,参数传递机制主要涉及到两个概念:值传递(Pass by Value)和引用传递(Pass by Reference)。
① 值传递(Pass by Value):在值传递机制中,函数调用时,实际参数的值会被复制一份给形式参数,函数内部对形式参数的修改不会影响到实际参数。这意味着函数内部对参数进行修改,不会改变原始的变量。
② 引用传递(Pass by Reference):在引用传递机制中,函数调用时,实际参数的引用(地址)会被传递给形式参数,函数内部对形式参数的修改会影响到实际参数。这意味着函数内部对参数进行修改,会改变原始的变量。
在 Python 中,参数传递机制是按对象引用进行传递的,也可以称之为对象引用传递。具体来说:
- 对于不可变对象(如数字、字符串、元组),采用值传递的方式。函数内对形式参数的修改不会影响到实际参数,因为不可变对象的值是无法被修改的。
- 对于可变对象(如列表、字典、集合),采用引用传递的方式。函数内对形式参数的修改会影响到实际参数,因为可变对象的值是可以被修改的。
即使在传递可变对象时采用引用传递,如果在函数内部对形式参数重新赋值,将会创建一个新的对象,并不会影响到实际参数。
30、Python对象基本要素有哪些?
有如下3点,id,type和value
31、比较两个对象的id和value有什么不同方式?
① 值比较(Value Comparison):用于判断两个对象的值是否相同,可以使用==运算符进行值比较。值比较是通过比较对象的内容来确定它们是否相等。
示例:
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # 输出:True
在上述示例中,a和b虽然是不同的对象,但它们的值是相同的,因此值比较的结果为True。
② 身份比较(Identity Comparison):用于判断两个对象是否是同一个对象,可以使用is运算符进行身份比较。身份比较是比较对象的内存地址来判断它们是否是同一个对象。
示例:
a = [1, 2, 3]
b = [1, 2, 3]
print(a is b) # 输出:False
在上述示例中,a和b是不同的对象,它们在内存中的地址是不同的,因此身份比较的结果为False。
对于小整数、字符串和布尔值这些不可变对象,解释器会对其进行缓存优化,使得它们的身份比较结果可能为True(解释器会先检查是否已经存在相同的对象,如果存在,则直接返回该对象的引用,而不是创建一个新的对象。),而对于大整数、列表等可变对象,解释器不会进行缓存优化,它们的身份比较结果通常为False。
32、__new__和__init__有什么区别?
__new__和__init__都是用于创建对象的特殊方法,它们在对象的生命周期中发挥不同的作用。
① __new__方法:
__new__是一个静态方法,用于创建对象并返回对象的实例。它在对象实例化之前被调用,并负责创建对象。
__new__方法接收类作为第一个参数(通常是cls),后续参数用于传递给__new__方法。
__new__方法的返回值通常是通过调用父类的__new__方法来获取的,然后对返回的实例进行一些自定义处理。
__new__方法常用于创建不可变对象或自定义元类,以及在子类化不可变类型时进行特殊处理。
② __init__方法:
__init__是一个实例方法,在对象实例化后被调用,用于初始化对象的状态。
__init__方法接收类的实例作为第一个参数(通常是self),后续参数用于传递给__init__方法。
__init__方法主要用于设置对象的初始属性值,执行一些必要的初始化操作。
__init__方法通常不返回任何值,它是在对象已经创建后被调用的。
综上所述,__new__方法负责创建对象实例,返回一个实例化的对象,而__init__方法负责对象的初始化工作,对已经创建的对象进行属性设置和其他必要的初始化操作。在创建对象时,首先调用__new__方法创建对象实例,然后调用__init__方法对对象进行初始化。
33、列出5个python标准库。
os:提供了不少与操作系统相关联的函数
sys: 通常用于命令行参数
re: 正则匹配
math: 数学运算
datetime:处理日期时间
34、Python有哪些常用的库?
① Pandas 库提供了数据整理、整理和分析这些数据科学最基本的需求。该库是能够读取、操作、聚合和可视化数据并将所有内容转换为易于理解的格式的全部内容。
② NumPy被广泛用作数组处理库。由于它可以管理多维数组对象,因此它被用作多维数据评估的容器。NumPy库由一系列的元素组成,每个元素都是相同的数据类型,一个正整数的元组理想地分隔了这些数据类型。维度称为轴,而轴的数量称为等级。NumPy 中的数组被归类为ndarray。
③ Scikit-Learn是Python 的本地机器学习库,它为数据科学家提供以下算法:支持向量机、随机森林、K-means 聚类、光谱聚类、均值偏移、交叉验证。这是我们可以使用此 Scikit-Learn 执行的操作分类、聚类、回归、降维、数据预处理。
④ Matplotlib可视化可以占据了数据的关键位置,它帮助我们创建2D 图形并将绘图用到应用程序中。
⑤ Seaborn另一个数据可视化库。Seaborn 与 Matplotlib 有何不同?尽管这两个软件包都作为数据可视化软件包,但实际区别在于您可以使用这两个库执行的可视化类型。对于初学者,使用 Matplotlib,我们只能创建基本图,包括条形、线条、区域、散点图等。但是,使用 Seaborn,可视化水平提高了一个档次,因为您可以用更少的资源创建各种复杂的可视化图形。
35、Python的os库和sys库有什么区别?
在Python中,os和sys是两个常用的标准库,用于处理操作系统相关的功能。虽然它们都与操作系统有关,但在具体功能和使用方式上存在一些区别。
os库:os库提供了许多与操作系统交互的函数,用于处理文件、目录、进程等操作。
sys库:sys库提供了与Python解释器相关的功能和变量,主要用于控制解释器和与解释器交互。
36、什么是lambda表达式?它有什么好处?
lambda表达式是一种用于创建匿名函数(需要一个函数,但又不想命名)的语法形式。它可以在代码中快速定义简单的函数,而无需使用def关键字来定义具名函数。
lambda表达式的一般语法为:lambda 参数: 表达式
其中,参数是函数的输入参数,而表达式则是函数的返回值。
lambda表达式可以包含多个参数,并且可以执行任意复杂的表达式。它适用于一些简单的、单行的函数操作,避免定义完整的具名函数。例如,在列表排序、过滤、映射等函数中,lambda表达式经常被用作简洁的函数参数。lambda表达式是一个表达式不是一个语句。
示例:
# 创建一个lambda表达式,计算传入参数的平方
square = lambda x: x**2
# 调用lambda表达式
result = square(5)
print(result) # 输出: 25
37、单下划线和双下划线分别代表什么?
单下划线和双下划线都是特殊的命名约定,用于标识变量、方法和类的属性的特殊含义。
① 单下划线 _:
前导单下划线:例如 var,表示这个变量或方法是一个内部使用的变量或方法,它不应该被外部直接访问。尽管可以通过引用来访问它们,但开发者应将其视为非公开的。
后缀单下划线:例如 var,用于避免与Python关键字冲突,以及与已有的名称产生歧义。
② 双下划线 __:
前导双下划线:例如 __var,表示名称修饰(name mangling),用于防止命名冲突。Python会将以双下划线开头的变量名替换为_Classname__var 的形式,其中 Classname 是包含该变量的类的名称。
后缀双下划线:例如 var__,通常没有特殊含义,只是作为一种命名风格而存在。
单下划线和双下划线的使用主要是一种约定,并不会在语言层面施加强制。它们的作用是提供一种命名规范,用于指示变量、方法和类的特殊含义,以及与其他命名产生区分。但在实际编码中,开发者仍然可以访问以单下划线或双下划线开头或结尾的名称,因为Python并不对其进行强制限制。
38、什么是元类? 在什么场景下使用?
元类是用于创建类的类。在Python中,一切皆对象,包括类本身。而元类就是用来创建这些类的“模板”。通过定义元类,可以控制类的创建过程,决定如何生成类和处理类的属性、方法等。
元类的主要作用有以下几个方面:
- 控制类的创建行为:通过定义元类,可以在类创建时动态地修改类的定义,添加新的属性、方法或者修改已有的属性、方法。这样可以实现一些高级的编程技巧和元编程技术。
- 类型检查和约束:元类可以用来检查类的定义是否符合特定的规范或约束,并在类创建时进行验证。这对于实现框架、库或者API的开发者来说是很有用的,可以确保用户创建的类满足特定的要求。
- 注册和管理类:元类可以用于注册和管理类的信息,比如在ORM(对象关系映射)框架中,元类可以用于将类与数据库表进行映射,并提供对数据库的操作。
- AOP(面向切面编程):元类可以在类的创建阶段,通过修改类的属性和方法,自动地为类添加一些横切关注点,比如日志、性能统计等。
使用元类的场景通常包括以下情况:
- 当需要对创建的类进行自定义的行为修改时,可以通过定义元类来实现。比如在类创建时添加一些公共的方法、属性,或者对类的定义进行验证和处理。
- 当想要实现特定的框架、库或者API时,元类可以用于控制类的创建行为,确保用户定义的类满足特定的要求。
- 在需要实现ORM框架、代码生成器等场景下,元类可以用于注册和管理类的信息,并根据类的定义进行相应的操作。
元类是一种高级的编程概念,在大多数情况下,使用普通的类就可以满足需求。只有在特定的场景下,才需要使用元类来实现更高级和灵活的功能。
39、什么是collections模块?
是Python标准库中的一个模块,提供了一些额外的数据类型和工具,用于扩展和增强内置的基本数据类型。它包含了一些有用的容器类,用于处理和操作各种数据结构。
collections模块中的主要数据类型和工具包括:
- namedtuple:命名元组(Named Tuple)是一个创建自定义的、可命名的元组的工厂函数。它类似于普通的元组,但可以通过属性名进行访问,而不仅仅是通过索引。命名元组在表示简单的数据对象时很有用。
- deque:双端队列(Deque)是一个线程安全、支持高效插入和删除操作的数据结构。它是一个双向链表,可以从队列的两端执行插入和删除操作,适用于需要频繁操作队列两端的场景。
- Counter:计数器(Counter)是一个字典的子类,用于计算可哈希对象的出现次数。它提供了方便的计数功能,可以快速统计列表、字符串等中元素的出现次数。
- OrderedDict:有序字典(Ordered Dictionary)是一个字典的子类,它记住了元素的插入顺序,并且可以按照插入顺序返回键值对。与普通的字典不同,有序字典在迭代时会保持元素的顺序。
- defaultdict:默认字典(Default Dictionary)是一个字典的子类,它提供了默认值的支持。当访问字典中不存在的键时,如果使用了defaultdict,将返回一个默认值而不抛出KeyError异常。
- ChainMap:链映射(Chain Map)是将多个字典或映射对象进行链接的工具。它可以将多个字典按照顺序链接为一个逻辑上的大字典,并提供了一致的字典视图。
除了上述常用的数据类型外,collections模块还包含其他一些辅助函数和数据结构,如Counter、defaultdict等,用于处理和操作各种常见的数据结构。这些工具和数据类型可以帮助我们更方便地处理数据和解决问题,提高编程效率。
40、谈一谈Python的自省机制。
Python的自省(introspection)机制指的是在运行时通过一些内置的函数和特性来获取对象的相关信息,包括对象类型、属性、方法等。自省机制可以帮助我们动态地理解和操作代码。
下面是一些常用的自省机制的示例:
- type()函数:type(obj)函数可以返回对象的类型。它可以用于确定一个对象是什么类型,包括基本数据类型(如int、str)和自定义类。
- dir()函数:dir(obj)函数返回一个包含对象所有属性和方法名称的列表。使用dir()函数可以查看对象有哪些属性和方法可用。
- getattr()、hasattr()和setattr()函数:这些函数分别用于获取、检查和设置对象的属性。例如,getattr(obj, ‘attr_name’)可以获取对象的属性,hasattr(obj, ‘attr_name’)可以检查对象是否具有某个属性,setattr(obj, ‘attr_name’, value)可以设置对象的属性值。
- isinstance()函数:isinstance(obj, class_or_tuple)函数用于检查一个对象是否是某个类或元组中的任何一个类的实例。它可以用于判断对象的类型是否满足特定条件。
- issubclass()函数:issubclass(class, classinfo)函数用于检查一个类是否是另一个类的子类。它可以用于判断类之间的继承关系。
- __dict__属性:__dict__是一个字典,包含对象的所有属性和对应的值。通过访问对象的__dict__属性,可以查看对象的属性和值。
- inspect模块:Python的inspect模块提供了更强大和灵活的自省功能。它可以用于获取对象的源代码、签名、注释等信息,还可以检查函数和类是否被定义在特定的模块中。
这些自省机制使得我们可以在运行时获取对象的各种信息,帮助我们动态地了解和操作代码。它们在调试、代码分析、动态扩展等方面非常有用。