题目41:说一下你对Python中模块和包的理解。

每个Python文件就是一个模块,而保存这些文件的文件夹就是一个包,但是这个作为Python包的文件夹必须要有一个名为__init__.py的文件,否则无法导入这个包。通常一个文件夹下还可以有子文件夹,这也就意味着一个包下还可以有子包,子包中的__init__.py并不是必须的。模块和包解决了Python中命名冲突的问题,不同的包下可以有同名的模块,不同的模块下可以有同名的变量、函数或类。在Python中可以使用importfrom ... import ...来导入包和模块,在导入的时候还可以使用as关键字对包、模块、类、函数、变量等进行别名,从而彻底解决编程中尤其是多人协作团队开发时的命名冲突问题。

题目42:说一下你知道的Python编码规范。

点评:企业的Python编码规范基本上是参照PEP-8或谷歌开源项目风格指南来制定的,后者还提到了可以使用Lint工具来检查代码的规范程度,面试的时候遇到这类问题,可以先说下这两个参照标准,然后挑重点说一下Python编码的注意事项。

空格的使用

  1. 使用空格来表示缩进而不要用制表符(Tab)。
  2. 和语法相关的每一层缩进都用4个空格来表示。
  3. 每行的字符数不要超过79个字符,如果表达式因太长而占据了多行,除了首行之外的其余各行都应该在正常的缩进宽度上再加上4个空格。
  4. 函数和类的定义,代码前后都要用两个空行进行分隔。
  5. 在同一个类中,各个方法之间应该用一个空行进行分隔。
  6. 二元运算符的左右两侧应该保留一个空格,而且只要一个空格就好。

标识符命名

  1. 变量、函数和属性应该使用小写字母来拼写,如果有多个单词就使用下划线进行连接。
  2. 类中受保护的实例属性,应该以一个下划线开头。
  3. 类中私有的实例属性,应该以两个下划线开头。
  4. 类和异常的命名,应该每个单词首字母大写。
  5. 模块级别的常量,应该采用全大写字母,如果有多个单词就用下划线进行连接。
  6. 类的实例方法,应该把第一个参数命名为self以表示对象自身。
  7. 类的类方法,应该把第一个参数命名为cls以表示该类自身。

表达式和语句

  1. 采用内联形式的否定词,而不要把否定词放在整个表达式的前面。例如:if a is not b就比if not a is b更容易让人理解。
  2. 不要用检查长度的方式来判断字符串、列表等是否为None或者没有元素,应该用if not x这样的写法来检查它。
  3. 就算if分支、for循环、except异常捕获等中只有一行代码,也不要将代码和ifforexcept等写在一起,分开写才会让代码更清晰。
  4. import语句总是放在文件开头的地方。
  5. 引入模块的时候,from math import sqrtimport math更好。
  6. 如果有多个import语句,应该将其分为三部分,从上到下分别是Python标准模块第三方模块自定义模块,每个部分内部应该按照模块名称的字母表顺序来排列。

题目43:运行下面的代码是否会报错,如果报错请说明哪里有什么样的错,如果不报错请说出代码的执行结果。



class A: 
    def __init__(self, value):
        self.__value = value

    @property
    def value(self):
        return self.__value

obj = A(1)
obj.__value = 2
print(obj.value)
print(obj.__value)



点评:这道题有两个考察点,一个考察点是对 ___开头的对象属性访问权限以及 @property装饰器的了解,另外一个考察的点是对动态语言的理解,不需要过多的解释。



1
2



扩展:如果不希望代码运行时动态的给对象添加新属性,可以在定义类时使用 __slots__魔法。例如,我们可以在上面的 A中添加一行 __slots__ = ('__value', ),再次运行上面的代码,将会在原来的第10行处产生 AttributeError错误。

题目44:对下面给出的字典按值从大到小对键进行排序。



prices = {
    'AAPL': 191.88,
    'GOOG': 1186.96,
    'IBM': 149.24,
    'ORCL': 48.44,
    'ACN': 166.89,
    'FB': 208.09,
    'SYMC': 21.29
}



点评sorted函数的高阶用法在面试的时候经常出现, key参数可以传入一个函数名或一个Lambda函数,该函数的返回值代表了在排序时比较元素的依据。



sorted(prices, key=lambda x: prices[x], reverse=True)



题目45:说一下namedtuple的用法和作用。

点评:Python标准库的 collections模块提供了很多有用的数据结构,这些内容并不是每个开发者都清楚,就比如题目问到的 namedtuple,在我参加过的面试中,90%的面试者都不能准确的说出它的作用和应用场景。此外, deque也是一个非常有用但又经常被忽视的类,还有 CounterOrderedDictdefaultdictUserDict等类,大家清楚它们的用法吗?

在使用面向对象编程语言的时候,定义类是最常见的一件事情,有的时候,我们会用到只有属性没有方法的类,这种类的对象通常只用于组织数据,并不能接收消息,所以我们把这种类称为数据类或者退化的类,就像C语言中的结构体那样。我们并不建议使用这种退化的类,在Python中可以用namedtuple(命名元组)来替代这种类。



from collections import namedtuple

Card = namedtuple('Card', ('suite', 'face'))
card1 = Card('红桃', 13)
card2 = Card('草花', 5)
print(f'{card1.suite}{card1.face}')
print(f'{card2.suite}{card2.face}')



命名元组与普通元组一样是不可变容器,一旦将数据存储在namedtuple的顶层属性中,数据就不能再修改了,也就意味着对象上的所有属性都遵循“一次写入,多次读取”的原则。和普通元组不同的是,命名元组中的数据有访问名称,可以通过名称而不是索引来获取保存的数据,不仅在操作上更加简单,代码的可读性也会更好。

命名元组的本质就是一个类,所以它还可以作为父类创建子类。除此之外,命名元组内置了一系列的方法,例如,可以通过_asdict方法将命名元组处理成字典,也可以通过_replace方法创建命名元组对象的浅拷贝。



class MyCard(Card):

    def show(self):
        faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
        return f'{self.suite}{faces[self.face]}'


print(Card)    # <class '__main__.Card'>
card3 = MyCard('方块', 12)
print(card3.show())    # 方块Q
print(dict(card1._asdict()))    # {'suite': '红桃', 'face': 13}
print(card2._replace(suite='方块'))    # Card(suite='方块', face=5)



总而言之,命名元组能更好的组织数据结构,让代码更加清晰和可读,在很多场景下是元组、字典和数据类的替代品。在需要创建占用空间更少的不可变类时,命名元组就是很好的选择。

温馨提示:Python面试宝典会持续更新,从基础到项目实战的内容都会慢慢覆盖到。虽然每天只更新5个题目,但是每道题扩散出的信息量还是比较大的,希望对找工作的小伙伴所有帮助。 你的点赞、收藏和评论都是我继续创建的动力,请不要吝惜你的赞美