面试总结 持续更新中

经典题型

1、PEP8规范


- 使用space(空格)来表示缩进,而不要用tab(制表符)
- 和语法相关的每一层缩进都要用4个空格来表示
- 每行的字符数不应超过79
- 对于占据多行的长表达式来说,除了首行之外的其余各行都应该在通常的缩进级别之上再加上4个空格
- 文件中函数与类之间应该用两个空行隔开
- 在同一个类中,各方法之间应该用一个空行隔开

2、了解bytes、str与unicode的区别

python3:  bytes、str
  """
bytes包含原始的8个bit位
str包含unicode字符
  """
python2:  str、unicode
  """
str包含8个bit位
unicode包含unicode字符
  """

unicode字符转换成二进制数据,必须使用encode方法。

二进制数据转换成unicode字符,必须使用decode方法。

"""
python3:
"""
def to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes):
        value = bytes_or_str.decode('utf-8')
    else:
        value = bytes_or_str
    return value


def to_bytes(bytes_or_str):
    if isinstance(bytes_or_str, str):
        value = bytes_or_str.encode('utf-8')
    else:
        value = bytes_or_str
    return value

print(to_bytes("你好"))


"""
python2
"""
def to_bytes(uncicode_or_str):
    if isinstance(uncicode_or_str, str):
        value = uncicode_or_str.decode('utf-8')
    else:
        value = uncicode_or_str
    return value


def to_unicode(uncicode_or_str):
    if isinstance(uncicode_or_str, unicode):
        value = uncicode_or_str.encode('utf-8')
    else:
        value = uncicode_or_str
    return value


print to_bytes('你好')
print to_unicode(u'你好')

总结:

# python2
print type('你好'.decode('utf-8'))  ==> unicode

print type('你好')  ==> str

# python3
print('你好'.decode('utf-8'))  ==> 报错

print(to_str('你好'))
  • python3中,bytes是一种包含8个bit的序列,str是一种包含unicode字符的序列。不能以>或+等操作符来操作
  • python2中,str是一种包含8个bit的序列,unicode是一种包含unicode字符的序列。如果str只含有7位ascii字符,那么可以通过相关操作符来同时使用时str与unicode

3、面试题

以下结果输出结果为多少?

def multipliers():
  return [lambda x : i * x for i in range(4)]

print([m(2) for m in multipliers()])
上面代码输出的结果是[6, 6, 6, 6] (不是我们想的[0, 2, 4, 6]).

产生这个结果的主要原因是python闭包的延迟绑定。这意味着内部函数被调用时,参数的值在闭包内进行查找.

当任何由multipliers()返回的函数被调用时,i的值将在附近的范围进行查找。那时,不管返回的函数是否被调用,for循环已经完成,i被赋予了最终的值3.

4、反转字典

给定一个字典,实现键和值的映射。

d = {'name':'张三','age':18,'sex':'male'}

如何将字典的键值映射反转过来?

解决方案:

1、列表推导式

d1 = dict([(v,k) for k,v in d.items()])

2、zip

对于比较大的字典,用python内置的zip方法会更快一些

d2 = dict(zip(d.values(),d.keys()))

4、列表索引范围

下面代码的输出结果是什么?

list = ['a', 'b', 'c', 'd', 'e']
print(list[10:])

执行结果:

[]

下面的代码将输出[],不会产生IndexError错误。就像所期望的那样,尝试用超出成员的个数的index来获取某个列表的成员

例如,尝试获取list[10]和之后的成员,会导致IndexError.

然而,尝试获取列表的切片,开始的index超过了成员个数不会产生IndexError,而是仅仅返回一个空列表。

这成为特别让人恶心的疑难杂症,因为运行的时候没有错误产生,导致bug很难被追踪到。

5、列表题型

各行打印的结果应该是多少?

list = [ [ ] ] * 5
list  # output?
list[0].append(10)
list  # output?
list[1].append(20)
list  # output?
list.append(30)
list  # output?

输出结果如下:

[[], [], [], [], []]
[[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]
第一行的输出结果直觉上很容易理解,例如 list = [ [ ] ] * 5 就是简单的创造了5个空列表。然而,理解表达式list=[ [ ] ] * 5的关键一点是它不是创造一个包含五个独立列表的列表,而是它是一个创建了包含对同一个列表五次引用的列表。只有了解了这一点,我们才能更好的理解接下来的输出结果。

6、列表索引的变化

lis = [1, 2, 3, 4, 5, 6, 7]
for i in lis:
    if i == 2:
        lis.remove(i)
    print(i)

print(lis)

#打印结果  发现少了个3,但是结果也是正确的
1
2
4
5
6
7
结果:[1, 3, 4, 5, 6, 7]

解决方法
lis = [1, 2, 3, 4, 5, 6, 7]
for i in lis[:]:   #切片,这样就不会改变原来的列表了
    if i == 2:
        lis.remove(i)
    print(i)

print(lis)

7、加速python查询速度

使用set而不是list进行查找

data = (i**2 + 1 for i in range(1000000))
list_data = list(data)
set_data = set(data)

先分别通过list和set生成不同数据类型的数据

先测试查询列表数据的运行时间:

%%time
108639 in list_data

CPU times: user 10 ms, sys: 205 µs, total: 10.3 ms
Wall time: 10.2 ms

测试集合数据的运行时间

%%time
108639 in set_data

CPU times: user 3 µs, sys: 1 µs, total: 4 µs
Wall time: 6.91 µs

用dict而不是两个list进行匹配

list_a = [2 * i - 1 for i in range(1000000)]
list_b = [i ** 2 for i in list_a]
dict_ab = dict(zip(list_a, list_b))

低俗方法

%%time
list_b[list_a.index(97685)]

CPU times: user 613 µs, sys: 1e+03 ns, total: 614 µs
Wall time: 618 µs
9542359225

高速方法

%%time
dict_ab.get(97685)

CPU times: user 5 µs, sys: 0 ns, total: 5 µs
Wall time: 7.87 µs
9542359225

8、面试题

用python实现统计一篇英文文章内每个单词的出现频率,
并返回出现频率最高的前10个单词及其出现次数。(标点符号忽略)

import re
from collections import Counter

with open("文件名", "r", encoding="utf-8") as fd:
    texts = fd.read()                         # 将文件的内容全部读取成一个字符串
    count = Counter(re.split(r"W+", texts))  # 以单词为分隔

result = count.most_common(10)                # 统计最常使用的前10个

print(result)

9、 init__和__new

"new"方法在python中是真正的构造方法,(创建并返回实例),通过这个方法可以产生一个”cls”对应的实例对象,所以说”new”方法一定要有返回。

"init"方法,是一个初始化的方法,”self”代表由类产生出来的实例对象,”init”将对这个对象进行相应的初始化操作。

重写new
如果(新式)类中没有重写”new”方法,Python默认是调用该类的直接父类的”new”方法来构造该类的实例,如果该类的父类也没有重写”new”,那么将一直按照同样的规则追溯至object的”new”方法,因为object是所有新式类的基类。

而如果新式类中重写了”new”方法,那么可以选择任意一个其他的新式类(必须是新式类,只有新式类有”new”,因为所有新式类都是从object派生)的”new”方法来创建实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环

class A(object):

    def __init__(self, *args, **kwargs):
        print(f"init:{self.__class__}")

    def __new__(cls, *args, **kwargs):
        print(f"new:{cls}")
        return object.__new__(cls, *args, **kwargs)  # 返回的必须是当前类的父类的new方法


a = A()

init的调用

“new”决定是否要使用该类的”init”方法,因为”new” 可以调用其他类的构造方法或者直接返回别的类创建的对象来作为本类的实例。
通常来说,新式类开始实例化时,”new”方法会返回cls(cls指代当前类)的实例,然后调用该类的”init”方法作为初始化方法,该方法接收这个实例(即self)作为自己的第一个参数,然后依次传入”new”方法中接收的位置参数和命名参数。
但是,如果”new”没有返回cls(即当前类)的实例,那么当前类的”init”方法是不会被调用的。

class A(object):

    def __init__(self, *args, **kwargs):
        print("Call __init__ from %s" % self.__class__)

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls, *args, **kwargs)
        print("Call __new__ for %s" % obj.__class__)
        return obj


class B(object):

    def __init__(self, *args, **kwargs):
        print("Call __init__ from %s" % self.__class__)

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(A, *args, **kwargs)
        print("Call __new__ for %s" % obj.__class__)
        return obj


b = B()
print(type(b))

10、派生不可变类型

关于”new”方法还有一个重要的用途就是用来派生不可变类型
例如,python中的float类型是一个不可变类型,如果想要从float中派生出一个子类,就可以使用”new”方法

class Round2Float(float):

    def __new__(cls, num):
        num = round(num, 2)

        return float.__new__(Round2Float, num)


f = Round2Float(1.234)
print(f)

# 上述代码可以从float中派生出一个Round2Float类,该类的实例就是保留两位小数

__new__总结(新式类才有__new__方法):

  • new” 方法是在类实例化对象时第一个调用的方法,将返回实例对象
  • new” 方法始终都是类的静态方法(即第一个参数为cls),即使没有被加上静态方法装饰器
  • 第一个参数cls是当前正在实例化的类,如果要得到当前类的实例,应当在当前类中的 “new” 方法语句中调用当前类的父类的” new” 方法。

11、Reduce()

reduce函数的用法和map很类似,也是一个函数f和一个list,但是函数的入口参数一定要是两个,reduce也是对每个元素进行反复调用,最后返回最终的值,而map是返回一个list

list_a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ]
def add(x,y):
    return  x + y
print(reduce(add,list_a))

# 输出:55

python3里面reduce已经从全局函数里面移除了,需要用的话要from functools import reduce

12、MySQL 常见数据库引擎及区别

show engines;  -- 查看所有存储引擎
set default_storage_engine=innodb;  -- 修改本次会话的默认存储引擎(重启后失效),只对本会话有效,其他会话无效:
set global default_storage_engine=innodb;  -- 修改全局会话默认存储引擎(重启后失效),对所有会话有效

希望重启后也生效的话,需要编辑/etc/my.cnf,[mysqld]下面任意位置添加配置;(所有对配置文件的修改,重启后生效)

default-storage-engine = InnoDB

MyISAM:

MyISAM是MySQL的ISAM扩展格式和缺省的数据库引擎。除了提供ISAM里所没有的索引和字段管理的大量功能,MyISAM还使用一种表格锁定的机制,来优化多个并发的读写操作,其代价是你需要经常运行OPTIMIZE TABLE命令,来恢复被更新机制所浪费的空间。MyISAM还有一些有用的扩展,例如用来修复数据库文件的MyISAMCHK工具和用来恢复浪费空间的 MyISAMPACK工具。MYISAM强调了快速读取操作,这可能就是为什么MySQL受到了WEB开发如此青睐的主要原因:在WEB开发中你所进行的大量数据操作都是读取操作。所以,大多数虚拟主机提供商和INTERNET平台提供商只允许使用MYISAM格式。MyISAM格式的一个重要缺陷就是不能在表损坏后恢复数据。

<div style="color:red"> 插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比较低,也可以使用。</div>

InnoDB:

InnoDB数据库引擎都是造就MySQL灵活性的技术的直接产品,这项技术就是MYSQL+API。在使用MYSQL的时候,你所面对的每一个挑战几乎都源于ISAM和MyISAM数据库引擎不支持事务处理(transaction process)也不支持外来键。尽管要比ISAM和 MyISAM引擎慢很多,但是InnoDB包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的。如前所述,如果你的设计需要这些特性中的一者或者两者,那你就要被迫使用后两个引擎中的一个了。

<div style="color:red;">支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。</div>

MEMORY:

MEMORY是MySQL中一类特殊的存储引擎。它使用存储在内存中的内容来创建表,而且数据全部放在内存中。这些特性与前面的两个很不同。每个基于MEMORY存储引擎的表实际对应一个磁盘文件。该文件的文件名与表名相同,类型为frm类型。该文件中只存储表的结构。而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。值得注意的是,服务器需要有足够的内存来维持MEMORY存储引擎的表的使用。如果不需要了,可以释放内存,甚至删除不需要的表。MEMORY默认使用哈希索引。速度比
使用B型树索引快。当然如果你想用B型树索引,可以在创建索引时指定。注意,MEMORY用到的很少,因为它是把数据存到内存中,如果内存出现异常就会影响数据。如果重启或者关机,所有数据都会消失。因此,基于MEMORY的表的生命周期很短,一般是一次性的。

<div style="color:red;">所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能
  建立太大的表。所以,这类数据库只使用在相对较小的数据库表。</div>

Archive:

适用于只有INSERT和SELECT操作,非常适合存储归档数据,比如记录日志信息。

![存储引擎](配图/存储引擎.png)

<div style="color:red;font-size:24px">**目前企业中使用最广泛的还是InnoDB存储引擎**</div>

13、 *args和**kwargs题型

输出结果?

def doff(arg1, *args):
    print(type(args))

doff("applea", "bananas", "cherry")

# 输出结果
<class 'tuple'>

*args,**kwargs的区别是什么?并且写出下边代码的输入内容


def foo(*args, **kwargs):
    print("args=", args)
    print("kwargs=", kwargs)
    print("-----------------")


foo(1, 2, 3, 4)
foo(a=1, b=2, c=3)
foo(1, 2, 3, 4, a=1, b=2, c=3)
foo("a", 1, None, a=1, b="2", c=3)


# 输出结果
args= (1, 2, 3, 4)
kwargs= {}
-----------------
args= ()
kwargs= {'a': 1, 'b': 2, 'c': 3}
-----------------
args= (1, 2, 3, 4)
kwargs= {'a': 1, 'b': 2, 'c': 3}
-----------------
args= ('a', 1, None)
kwargs= {'a': 1, 'b': '2', 'c': 3}
-----------------
原文地址:https://www.cnblogs.com/bladecheng/p/11183444.html