python笔试题(3)

以下是我在学习Python时经常遇到的几个问题,为了防止自己忘记,我在这里进行了整理。

[En]

Here are several questions I often encounter in learning Python, which are sorted out here to prevent myself from forgetting.

1,python中@property的用法

(学习于廖雪峰:https://www.liaoxuefeng.com/wiki/1016959663602400/1017502538658208)

在Python中,可以通过@property (装饰器)将一个方法转换为属性,从而实现用于计算的属性。将方法转换为属性后,可以直接通过方法名来访问方法,而不需要一对小括号”()”,这样可以让代码更加简洁。

绑定属性时,如果直接公开属性,虽然编写简单,但无法检查参数,导致对对象的任意赋值,如:

[En]

When binding a property, if we directly expose the property, although it is easy to write, there is no way to check the parameters, resulting in arbitrary assignment to the object, such as:

csharp;gutter:true; s = Student() s.score = 9999</p> <pre><code> 这显然不合逻辑,为了限制score的范围,可以通过一个 set_score() 方法来设置成绩,再通过一个 get_score() 来获取成绩,这样,在 set_score() 方法里,就可以检查参数: ;gutter:true;
class Student(object):

def get_score(self):
return self._score

def set_score(self, value):
if not isinstance(value, int):
raise ValueError("score must be an integer")
if value < 0 or value > 100:
raise ValueError("score must between 0~100")
self._score = value

现在,如果您对任何学生实例进行操作,则不能随意设置分数。

[En]

Now, if you operate on any Student instance, you can’t set score at will.

csharp;gutter:true; s = Student() s.set_score(60) print(s.get_score()) s.set_score(2123) print(s.get_score()) ''' 60 Traceback (most recent call last): ... ...</p> <pre><code>raise ValueError("score must between 0~100") </code></pre> <p>ValueError: score must between 0~100'''</p> <pre><code> 但是,上面的调用方法稍微复杂一些,不像直接使用属性那样简单。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>However, the above invocation method is slightly more complex and is not as straightforward as using attributes directly.</font>*</details> 有没有一种方法可以使用属性等简单方法来检查参数和访问类的变量?对于追求完美的Python程序员来说,这是必须做到的。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>Is there a way to check parameters and access variables of a class using simple methods such as properties? For Python programmers who pursue perfection, this must be done.</font>*</details> 还记得修饰者可以动态地向函数添加函数吗?装饰符还为类的方法工作。Python内置的@Property修饰器负责调用方法编程属性。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>Remember that decorator can add functions to functions dynamically? The decorator also works for the methods of the class. Python's built-in @ property decorator is responsible for calling a method programming property.</font>*</details> ;gutter:true;
class Student(object):

@property
def score(self):
return self._score

@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError("score must be an integer")
if value < 0 or value > 100:
raise ValueError("score must between 0~100")
self._score = value

@property 的实现比较复杂,我们先考察如何使用。把一个 getter方法编程属性,只需要加上 @property 就可以了,此时,@property本身又创建了另一个装饰器 @score.setter ,负责把一个 setter 方法变成属性赋值,于是,我们就拥有一个可控的属性操作:

csharp;gutter:true; s = Student() s.score = 60 print(s.score) s.score = 1232 print(s.score) ''' 60 Traceback (most recent call last): ... ...</p> <pre><code>raise ValueError("score must between 0~100") </code></pre> <p>ValueError: score must between 0~100'''</p> <pre><code> 注意到这个神奇的@属性,我们知道当我们在实例属性上操作时,它很可能不会直接公开,而是通过getter和setter方法公开。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>Noticing this magical @ property, we know that when we operate on an instance property, it is likely not to be exposed directly, but through the getter and setter methods.</font>*</details> 您还可以定义只读属性,只定义getter方法,而不定义setter方法是只读属性:<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>You can also define a read-only property, defining only the getter method, and not defining the setter method is a read-only property:</font>*</details> ;gutter:true;
class Student(object):

@property
def birth(self):
return self._birth

@birth.setter
def birth(self, value):
self._birth = value

@property
def age(self):
return 2015 – self._birth

上面的 birth 是可读写属性,而 age 就是一个只读属性,因为 age 可以根据 birth 和当前时间计算出来的。

所以 @property 广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

@property为属性添加安全保护机制

在Python中,默认情况下,创建的类属性或者实例是可以在类外进行修改的,如果想要限制其不能再类体外修改,可以将其设置为私有的,但设置为私有后,在类体外也不能获取他的值,如果想要创建一个可以读但不能修改的属性,那么也可以用@property 实现只读属性。

注意:私有属性__name 为单下划线,当然也可以定义为 name,但是为什么定义_name?

以一个下划线开头的实例变量名,比如 _age,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当看到这样的变量时,意思是: 虽然可以被访问,但是,请视为私有变量,不要随意访问

2,Python中类继承object和不继承object的区别

当我们编写代码时,我们经常会发现,在定义类时,有些代码继承对象,有些代码不继承对象,那么有对象和没有对象有什么区别呢?

[En]

When we write code, we often find that when we define class, some code inherits object and some code does not inherit object, so what is the difference between having object and not having object?

让我们来看看,在Python2.x和Python3.x中,不同的类分别通过继承Object和不继承Object来定义,然后分别通过dir()和type来查看这个类的所有方法和类型:

[En]

Let’s see that in Python2.x and Python3.x, different classes are defined by inheriting object and not inheriting object, respectively, and then all the methods and types of this class are viewed through dir () and type, respectively:

Python2.X

csharp;gutter:true;</p> <blockquote> <blockquote> <blockquote> <p>class test(object): ... pass ...</p> <p>dir(test) ['<strong>class</strong>', '<strong>delattr</strong>', '<strong>dict</strong>', '<strong>doc</strong>', '<strong>format</strong>', '<strong>getattribute</strong>', '<strong>hash</strong>', '_ _init__', '<strong>module</strong>', '<strong>new</strong>', '<strong>reduce</strong>', '<strong>reduce_ex</strong>', '<strong>repr</strong>', '<strong>setattr</strong>', '<strong>size of</strong>', '<strong>str</strong>', '<strong>subclasshook</strong>', '<strong>weakref</strong>'] type(test)</p> <p>class test2(): ... pass ...</p> <p>dir(test2) ['<strong>doc</strong>', '<strong>module</strong>'] type(test2)</p> </blockquote> </blockquote> </blockquote> <pre><code> Python3.X ;gutter:true;
>>> class test(object):
    pass

>>> class test2():
    pass

>>> type(test)

>>> type(test2)

>>> dir(test)

[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’]

>>> dir(test2)

[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’]

所以:在Python3.X中,继承和不继承object对象,我们创建的类,均拥有好多可操作对象。所以不用管。而python2.X中,却不一样。那这是为什么呢?

这需要从新课程和古典课程开始。

[En]

This needs to start with new classes and classical classes.

csharp;gutter:true;</p> <h1>新式类是指继承object的类</h1> <p>class A(obect): pass</p> <h1>经典类是指没有继承object的类</h1> <p>class A: pass</p> <pre><code> Python中推荐大家使用新式类(新式类已经兼容经典类,修复了经典类中多继承出现的bug) #### 那经典类中多继承bug是什么呢? (参考文献:https://www.cnblogs.com/attitudeY/p/6789370.html) 让我们来看看下面的图片:<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>Let's take a look at the following picture:</font>*</details> ![](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/rewrite/1226410-20191212094846107-1445526839.png) BC为A的子类,D为BC的子类,A中有save方法,C对其进行了重写。 在经典类中,调用D的SAVE方法,按深度优先搜索,路径B-A-C,在A中执行SAVE显然是不合理的。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>In the classical class, it is obviously unreasonable to call the save method of D, search by depth first, path B-A-C, and execute save in A.</font>*</details> 在新类中,调用D的保存方法,按广度优先搜索,路径B-C-A,在C中执行保存<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>In the new class, call the save method of D, search by breadth first, path B-C-A, and execute save in C</font>*</details> 代码: ;gutter:true;
#经典类
class A:
def __init__(self):
print ‘this is A’

def save(self):
print ‘come from A’

class B(A):
def __init__(self):
print ‘this is B’

class C(A):
def __init__(self):
print ‘this is C’
def save(self):
print ‘come from C’

class D(B,C):
def __init__(self):
print ‘this is D’

d1=D()
d1.save() #结果为’come from A

#新式类
class A(object):
def __init__(self):
print ‘this is A’

def save(self):
print ‘come from A’

class B(A):
def __init__(self):
print ‘this is B’

class C(A):
def __init__(self):
print ‘this is C’
def save(self):
print ‘come from C’

class D(B,C):
def __init__(self):
print ‘this is D’

d1=D()
d1.save() #结果为’come from C’

所以,Python3.X 中不支持经典类了,虽然还是可以不带object,但是均统一使用新式类,所以在python3.X 中不需要考虑object的继承与否。但是在 Python 2.7 中这种差异仍然存在,因此还是推荐使用新式类,要继承 object 类。

3,深度优先遍历和广度优先遍历

图的遍历是指从图中某一顶点除法访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历。

深度优先遍历的常见数据结构是堆栈,广度优先遍历的常见数据结构是队列。

[En]

The common data structure of depth-first traversal is stack, and the common data structure of breadth-first traversal is queue.

3.1 深度优先遍历Depth First Search(DFS)

深度优先遍历的思想是自上而下地遍历每个分支,向下遍历每个分支直到分支的末端,然后返回到上层,继续搜索右子树,直到遍历完整个树。因此,深度搜索步骤符合堆栈的后进先出的特点。

[En]

The idea of depth-first traversal is from top to bottom, traversing each branch down to the end of the branch, then returning to the upper level, and continuing to search the right subtree until the whole tree is traversed completely. therefore, the step of deep search accords with the characteristics of last-in-first-out of the stack.

深度优先搜索算法:并非所有节点都被保留,占用空间较少;存在回溯操作(即入栈和出栈操作),运行速度较慢。

[En]

Depth-first search algorithm: not all nodes are reserved and take up less space; there are backtracking operations (that is, stack-in and out-stack operations), and the running speed is slow.

事实上,二叉树的前序、中序和后序遍历在本质上也可以视为深度优先遍历。

[En]

In fact, the pre-order, middle-order and post-order traversal of the binary tree can also be regarded as depth-first traversal in essence.

3.2 广度优先遍历Breadth First Search(BFS)

广度优先遍历的思想是从左到右,依次遍历树的每一层的所有节点。当一个层的节点完全遍历时,它开始遍历下一层,而下一层的节点恰好是上层的子节点。因此,宽搜索的步骤符合队列中的先进先出的思想。

[En]

The idea of breadth-first traversal is from left to right, traversing all the nodes of each layer of the tree in turn. When the node of one layer traverses completely, it begins to traverse the next layer, and the node of the next layer happens to be the child node of the upper layer. Therefore, the steps of wide search are in line with the idea of first-in-first-out in the queue.

广度优先搜索算法:保留所有节点,占用较大空间;无回溯操作(即无栈操作,出栈操作),运行速度快。

[En]

Breadth-first search algorithm: retain all nodes, take up a large space; no backtracking operation (that is, no stack operation, out of the stack operation), running fast.

事实上,二叉树的层次遍历本质上也可以看作是广度优先遍历。

[En]

In fact, the hierarchical traversal of binary tree can also be regarded as breadth-first traversal in essence.

4,Python 逻辑运算符 异或 xor

首先,让我们看看使用真值表的XOR逻辑:

[En]

First, let’s take a look at the XOR logic using the truth table:

python笔试题(3)

也就是说:

AB有一个为真,但不同时为真的运算称作异或

这看起来简单明了,但是如果我们将布尔值之间的XOR替换为数字之间的XOR,会发生什么呢?

[En]

It seems simple and straightforward, but what happens if we replace XOR between Boolean values with XOR between numbers?

让我们试一试。

[En]

Let’s have a try.

csharp;gutter:true; 0 ^ 0 0 ^ 1 1 ^ 0 1 ^ 1</p> <blockquote> <blockquote> <blockquote> <p>0 1 1 0</p> </blockquote> </blockquote> </blockquote> <pre><code> 结果表明,相同个数的XOR值为0,不同个数的XOR值为1。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>The result tells us that the number is the same XOR value is 0, the number is different XOR value is 1.</font>*</details> 让我们再试试0, 1 除外的数字 ;gutter:true;
5 ^ 3
>>>6

为什么答案是6呢?(其中这里 经历了几次计算,下面学习一下)

异或是 基于二进制基础上按位异或的结果计算 5^3 的过程,其实是将5和3分别转成二进制,然后进行异或计算。

csharp;gutter:true; 5 = 0101(b)</p> <p>3 = 0011(b)</p> <pre><code> 按照位 异或: ![](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/rewrite/1226410-20200415163044145-1527329188.png) ;gutter:true;
0^0 ->0

1^0 ->1

0^1 ->1

1^1 ->0

排起来就是 0110(b) 转换为十进制就是 6

python笔试题(3)

注意上面: 如果a,b两个值不相同,则异或结果为1,如果两个值相同,则异或结果为0

5,Python的位运算

位运算:就是对该数据的二进制形式进行运算操作。

位操作分为六种情况,如下所示:

[En]

Bit operations are divided into six cases, as follows:

python笔试题(3)

我们可以看一下1:10的十进制到二进制的结果:

[En]

We can take a look at the decimal-to-binary result of 1: 10:

python笔试题(3)

python笔试题(3)

左移运算

左移运算符:操作数的所有二进制都左移“<”

[En]

Left shift operator: all binaries of operands are shifted to the left by “<“

规则很简单:

[En]

The rule is simply:

python笔试题(3)

举个例子:

csharp;gutter:true;</p> <h1>运算数的二进制左移若干位,高位丢弃,低维补0</h1> <p>a = 2 其二进制为:0b10</p> <p>a << 2 : 将a移动两位: ob1000 a << n = a<em>(2</em>*n) =8 8的二进制为: 0b1000</p> <p>a << 4 : 将a移动两位: ob100000 a << n = a<em>(2</em>*n) =32 32的二进制为: 0b10000</p> <p>a << 5 : 将a移动两位: ob1000000 a << n = a<em>(2</em>*n) =64 64的二进制为: 0b100000</p> <pre><code> **右移运算** **右移运算符**:把">>" 左边的运算数的各二进制全部右移若干位,">>" 右边的数指定移动的位数。 **正数补0,负数补**1。 规则很简单:<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>The rule is simply:</font>*</details> ![](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/rewrite/1226410-20201222111618438-1753998517.png) 举个例子: ;gutter:true;
# 把">>" 左边的运算数的各二进位全部右移若干位,">>" 右边的数指定移动的位数。
# 正数补0,负数补1

a = 64 其二进制为:0b1000000

a >> 2 : 将a移动两位: ob10000
a >> n = a%(2**n) =16 16的二进制为: 0b10000

a >> 4 : 将a移动两位: ob100
a >> n = a%(2**n) =4 4的二进制为: 0b100

a >> 5 : 将a移动两位: ob10
a >> n = a%(2**n) =2 2的二进制为: 0b10

注:向左和向右移动是不同的,但向右移动更复杂。

[En]

Note: moving left and right is different, but moving right is more complicated.

按位与

按位与运算符:参与运算的两个值,如果两个对应的位都为1,则该位的结果为1,否则为0。

[En]

Bitwise and operator: two values that participate in the operation, if both corresponding bits are 1, the result of that bit is 1, otherwise it is 0.

举个例子:

csharp;gutter:true;</p> <h1>参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0。</h1> <p>a = 2 其二进制为:0b10 b = 4 其二进制为:0b100</p> <p>a & b 010 & 100 000 print(int('0', 2)) 0 # 0的二进制为 ob0</p> <pre><code> **按位或** 按位或运算符:只要对应的两个二进制位中的一个为1,则结果为1,否则为0。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>Bitwise or operator: as long as one of the corresponding two binary bits is 1, the result is 1, otherwise it is 0.</font>*</details> 举个例子: ;gutter:true;
# 按或运算的两个值,只要对应的两个二进位有一个为1时,则该位的结果为1,否则为0。

a = 2 其二进制为:0b10
b = 4 其二进制为:0b100

a | b 010 | 100 110
print(int(‘110’, 2)) 6 # 6的二进制为 ob110

按位异或

按位异或运算符:当对应的二进制不同时,结果为1,否则为0。

[En]

Bitwise XOR operator: when the corresponding binary is different, the result is 1, otherwise it is 0.

举个例子:

csharp;gutter:true;</p> <h1>按位异或运算的两个值,当对应的两个二进位相异时,则该位的结果为1,否则为0。</h1> <p>a = 2 其二进制为:0b10 b = 4 其二进制为:0b100</p> <p>a ^ b 010 ^ 100 110 print(int('110', 2)) 6 # 0的二进制为 ob110</p> <pre><code> **按位取反** 按位逆运算符:将数据的每个二进制值加1,~x类似于-x-1<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>Bitwise inverse operator: add 1 to each binary value of the data, ~ x is similar to-xMel 1</font>*</details> 举个例子: ;gutter:true;
# 按位取反运算符:对数据的每个二进制位取反,即把 1 变为 0,把 0 变为 1。

a = 2 其二进制为:0b10
(a+1)*(-1) = -3 其二进制为:-0b11

res = ~a

二进制转十进制

二进制到十进制的方法很简单:每一位都乘以2的幂(数字-1)。

[En]

The binary-to-decimal method is simple: each bit is multiplied by the power of 2 (digits-1).

csharp;gutter:true;</p> <h1>二进制转为十进制,每一位乘以2的(位数-1)次方</h1> <h1>如下:</h1> <pre><code>比如4,其二进制为 ob100, 那么 100 = 1*2**(3-1) + 0*2**(2-1) + 0*2**(1-1) = 4 </code></pre> <pre><code> 这些位被拆分,每个位被乘以2的(位数-1)的幂。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>The bits are taken apart and each bit is multiplied by 2 to the power of (digits-1).</font>*</details> **十进制转二进制** 十进制转换为二进制时,采用“除2取余数,倒序排列”的方法。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>When the decimal system is converted to binary, the method of "dividing 2 to take the remainder and arranging it in reverse order" is adopted.</font>*</details> 1. 用 2 整除十进制数,得到商和余数 2. 再用 2 整数商,得到新的商和余数 3. 重复第一步和第二步,直到商为0 4. 将先得到的余数作为二进制数的高位,后得到的余数作为二进制数的低位,依次排序 如下: ;gutter:true;
# 十进制转二进制 采用 除2取余,逆序排列
# 比如 101 的二进制 ‘0b1100101’

101 % 2 = 50 余 1
50 % 2 = 25 余 0
25 % 2 = 12 余 1
12 % 2 = 6 余 0
6 % 2 = 3 余 0
3 % 2 = 1 余 1
1 % 2 = 0 余 1

# 所以 逆序排列即二进制中的从高位到低位排序:得到7位数的二进制数为 ob1100101

6,collections.defaltdict()的用法

下面我们来看一下源代码说明:

[En]

Let’s take a look at the source code explanation:

csharp;gutter:true; class defaultdict(dict): """ defaultdict(default_factory[, ...]) --> dict with default factory</p> <pre><code>The default factory is called without arguments to produce a new value when a key is not present, in __getitem__ only. A defaultdict compares equal to a dict with the same items. All remaining arguments are treated the same as if they were passed to the dict constructor, including keyword arguments. </code></pre> <p>"""</p> <pre><code> 就是说collections类中的 defaultdict() 方法为字典提供默认值。和字典的功能是一样的。函数返回与字典类似的对象。只不过defaultdict() 是Python内建字典(dict)的一个子类,它重写了方法_missing_(key) ,增加了一个可写的实例变量 default_factory,实例变量 default_factory 被 missing() 方法使用,如果该变量存在,则用以初始化构造器,如果没有,则为None。其他的功能和 dict一样。 与字典相比,它最好的一点是,当使用dict时,如果引入的键不存在,将抛出KeyError。如果您想在key不存在时返回默认值,可以使用defaultdict。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>The best thing about it over dictionaries is that when using dict, if the introduced Key does not exist, the KeyError will be thrown. If you want to return a default value when Key does not exist, you can use defaultdict.</font>*</details> ;gutter:true;
from collections import defaultdict

res = defaultdict(lambda: 1)
res[‘key1’] = ‘value1’ # key1存在 key2不存在
print(res[‘key1’], res[‘key2’])
# value1 1
# 从结果来看,key2不存在,则返回默认值
# 注意默认值是在调用函数返回的,而函数在创建defaultdict对象时就已经传入了。
# 除了在Key不存在返回默认值,defaultdict的其他行为与dict是完全一致的

7,Python中 pyc文件学习

参考地址:https://blog.csdn.net/newchitu/article/details/82840767

这需要从头开始。

[En]

This needs to start from the beginning.

7.1 Python为什么是一门解释型语言?

初学Python时,听到的关于 Python的第一句话就是,Python是一门解释型语言,直到发现了 .pyc 文件的存在。如果是解释型语言,那么生成的 .pyc 文件是什么呢?

c 应该是 compiled 的缩写,那么Python是解释型语言如何解释呢?下面先来看看编译型语言与解释型语言的区别。

7.2 编译型语言与解释型语言

计算机不能识别高级语言,所以当我们运行高级语言程序时,我们需要一个“翻译器”将高级语言转换成计算机可以阅读的机器语言。这一过程分为两类,第一类是编译,第二类是口译。

[En]

Computers cannot recognize high-level languages, so when we run a high-level language program, we need a “translator” to transform the high-level language into a machine language that the computer can read. This process is divided into two categories, the first is compilation and the second is interpretation.

编译语言在执行程序之前,编译器首先会执行程序的编译过程,将程序变成机器语言。不需要在运行时进行转换,只需执行它。最典型的例子是C语言。

[En]

Compiled language before the execution of the program, the compiler will first perform a process of compilation of the program, turning the program into a machine language. There is no need for translation at run time, but just execute it. The most typical example is C language.

解释语言没有这种编译过程,但当程序执行时,程序由解释器逐行解释,然后直接运行。最典型的例子是Ruby。

[En]

Interpretive language does not have this compilation process, but when the program is executed, the program is explained line by line by the interpreter and then run directly. The most typical example is Ruby.

因此,编译后的语言在程序运行前对其进行了一次“翻译”,因此在运行时减少了“翻译”的过程,效率更高。然而,我们不能一概而论,一些解释语言在翻译程序时也可以通过解释器的优化来优化整个程序,从而在效率上接近编译语言。

[En]

Therefore, the compiled language has made a “translation” of the program before it runs, so the process of “translation” is reduced at run time, so it is more efficient. However, we can not generalize, some interpretive languages can also optimize the whole program when translating the program through the optimization of the interpreter, so as to approach the compiled language in efficiency.

此外,随着Java等基于虚拟机的语言的兴起,我们又不能把语言纯粹的分为解释型和编译型两种,用Java来说,java就是首先通过编译器编译成字节码文件,然后在运行时通过解释器给解释成及其文件,所以说java是一种先编译后解释的语言。

7.3 Python是什么呢?

其实Python和Java/ C# 一样,也是一门基于虚拟机的语言。

当我们在命令行中输入pythontest.py时,我们实际上激活了Python的“解释器”,告诉“解释器”是时候开始工作了。

[En]

When we type python test.py on the command line, we actually activate the “interpreter” of Python, telling the “interpreter” that it is time to start working.

可是在”解释”之前,其实执行的第一项工作和java一样,是编译。

所以Python是一门先编译后解释的语言。

Python在解释源码程序时分为两步:

如下图所示:步骤1:将源代码编译成字节码;步骤2:将字节码解释为机器码。

[En]

As shown in the following figure: step 1: compile the source code into bytecode; step 2: interpret the bytecode as machine code.

python笔试题(3)

当我们的程序没有被修改时,那么当我们下次运行程序时,我们可以跳过从源代码到字节码的过程,直接加载PYC文件,这可以加快启动速度。

[En]

When our program has not been modified, then the next time we run the program, we can skip the process from source code to bytecode and load the pyc file directly, which can speed up the startup.

7.4 简述Python的运行过程

在学习Python的运行过程之前,先说两个概念,PyCodeObject和pyc文件。

其实PyCodeObject 是Python编译器真正编译成的结果。

当Python程序运行时,编译的结果则是保存在位于内存中的 PyCodeObject中,当Python程序运行结束时,Python解释器则将 PyCodeObject写回pyc文件中。

当Python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复上面的过程。

所以我们应该用这种方式定位PyCodeObject和PYC文件,我们说PYC文件实际上是保存PyCodeObject的一种持久方式。

[En]

So we should locate PyCodeObject and pyc files in this way, and we say that Pyc files are actually a persistent way of saving PyCodeObject.

7.5 Python中单个pyc文件的生成

pyc文件是当我们 import 别的 py文件时,那个 py文件会被存一份 pyc 加速下次装载,而主文件因为只需要装载一次就没有存 pyc。

python笔试题(3)

比如上面的例子,从上图我们可以看出 test_b.py 是被引用的文件,所以它会在__pycache_ 下生成 test_b.py 对应的 pyc 文件,若下次执行脚本时,若解释器发现你的 .py 脚本没有变更,便会跳出编译这一步,直接运行保存在 pycache 目录下的 .pyc文件。

7.6 Python中pyc文件的批量生成

编译一个目录中的所有py文件。Python提供了一个名为complisteall的模板。代码如下:

[En]

Compile all the py files in a directory. Python provides a template called compileall. The code is as follows:

python笔试题(3)

通过这种方式,您可以批量生成所有文件的PYC。

[En]

In this way, you can batch generate pyc for all files.

python笔试题(3)

命令行为:

csharp;gutter:true; python -m compileall</p> <pre><code> #### 7.7 Python中pyc文件的注意点 使用 pyc文件可以跨平台部署,这样就不会暴露源码。 * 1,import 过的文件才会自动生成 pyc文件 * 2,pyc文件不可以直接看到源码,但是可以被反编译 * 3,pyc的内容,是跟-Python版本相关的,不同版本编译后的pyc文件是不同的,即3.6编译的文件在 3.5中无法执行。 #### 7.8 Python中pyc文件的反编译 使用 uncompyle6, 可以将 Python字节码转换回等效的 Python源代码,它接受python1.3 到 3.8 版本的字节码。 安装代码: ;gutter:true;
pip install uncompyle6

使用示例:

csharp;gutter:true;</p> <h1>反编译 test_a.pyc 文件,输出为 test_a.py 源码文件</h1> <p>uncompyle6 test_a.py test_a.cpython-36.pyc</p> <pre><code> ![](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/rewrite/1226410-20210111095334379-380524950.png) 源代码如下: ![](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/rewrite/1226410-20210111095434749-1746709150.png) 感觉反编译对于简单的文件来说是非常好的。我反编译了复杂的代码,发现它也可以出现,但一些代码不会出现,比如注释。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>It feels like decompilation is very OK for simple files. I decompiled the complex code and found that it could also appear, but some code such as comments would not appear.</font>*</details> ### 8,super() 函数 参考地址:https://www.runoob.com/python/python-func-super.html #### 8.1 super()函数描述 super() 函数是用于调用父类(超类)的一个方法。根据官方文档,super函数返回一个委托类 type 的父类或者兄弟类方法调用的代理对象。super 函数用来调用已经在子类中重写过的父类方法。 python3是直接调用 super(),这其实是 super(type, obj)的简写方式。 super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO),重复调用(钻石继承)等种种问题。 #### 8.2 单继承 在单继承中,super().__init__() 与 Parent.__init__() 是一样的。super()避免了基类的显式调用。 让我们来看一个例子来验证结果是否一致:<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>Let's take a look at an example to verify whether the results are consistent:</font>*</details> ;gutter:true;
# -*- coding:utf-8 -*-

# 下面两种class 的方法一样,现在建议使用第二种,毕竟使用新式类比较方便
# class FooParent(object):
class FooParent:
def __init__(self):
print(‘this is parent’)

def bar(self, message):
print(‘%s from parent’%message)
print(‘parent bar function’)

class FooChild(FooParent):
def __init__(self):
# 方法一
# super(FooChild, self).__init__() # 在Python3中写成 super().__init()
# 方法二
FooParent.__init__(self)
print(‘this is child’)

if __name__ == ‘__main__’:
foochild = FooChild()
foochild.bar(‘message’)
”’
方法1结果:
this is parent
this is child
message from parent
parent bar function

方法2结果:
this is parent
this is child
message from parent
parent bar function
”’

8.3 多继承

super 与父类没有实质性的关联。在单继承时,super获取的类刚好是父类,但多继承时,super获取的是继承顺序中的下一个类。

以下面的继承为例:

[En]

Take the following inheritance as an example:

csharp;gutter:true; Parent / \ / \ child1 child2 \ / \ / grandchild</p> <pre><code> 使用super,代码如下: ;gutter:true;
# -*- coding:utf-8 -*-

# 下面两种class 的方法一样,现在建议使用第二种,毕竟使用新式类比较方便
# class FooParent(object):
class FooParent:
def __init__(self):
print(‘this is parent’)

def bar(self, message):
print(‘%s from parent’%message)
print(‘parent bar function’)

class FooChild1(FooParent):
def __init__(self):
# 方法一
# super(FooChild1, self).__init__() # 在Python3中写成 super().__init()
# 方法二
FooParent().__init__()
print(‘this is child1111’)

class FooChild2(FooParent):
def __init__(self):
# 方法一
# super(FooChild2, self).__init__() # 在Python3中写成 super().__init()
# 方法二
FooParent().__init__()
print(‘this is child2222’)

class FooGrandchild(FooChild1, FooChild2):
def __init__(self):
# 方法一
#super(FooGrandchild, self).__init__() # 在Python3中写成 super().__init()
# 方法二
FooChild1().__init__()
FooChild2().__init__()
print(‘this is FooGrandchild’)

if __name__ == ‘__main__’:
FooChild1 = FooChild1()
FooChild1.bar(‘message’)
”’
方法1结果:
this is parent
this is child2222
this is child1111
this is FooGrandchild
message from parent
parent bar function

方法2结果:
this is parent
this is parent
this is child1111
this is parent
this is parent
this is child1111
this is parent
this is parent
this is child2222
this is parent
this is parent
this is child2222
this is FooGrandchild
message from parent
parent bar function
为了方便查看,我们打印child1的结果如下:
this is parent
this is parent
this is child1111
message from parent
parent bar function
”’

您可以看到,如果不使用Super,基类将被多次调用,这是非常昂贵的。

[En]

You can see that if you don’t use super, the base class will be called multiple times, which is very expensive.

最后一个例子:

[En]

The last example:

csharp;gutter:true;</p> <h1>-<em>- coding:utf-8 -</em>-</h1> <h1>下面两种class 的方法一样,现在建议使用第二种,毕竟使用新式类比较方便</h1> <h1>class FooParent(object):</h1> <p>class FooParent: def <strong>init</strong>(self): self.parent = "I'm the parent" print('this is parent')</p> <pre><code>def bar(self, message): print('%s from parent'%message) </code></pre> <p>class FooChild(FooParent): def <strong>init</strong>(self): # super(FooChild, self)首先找到FooChild的父类 # 就是类 FooParent,然后把类 FooChild的对象转换为类 FooParent 的对象 super(FooChild, self).<strong>init</strong>() print('this is child')</p> <pre><code>def bar(self, message): super(FooChild, self).bar(message) print('Child bar function') </code></pre> <p>if <strong>name</strong> == '<strong>main</strong>': foochild = FooChild() foochild.bar('durant is my favorite player') ''' this is parent this is child durant is my favorite player from parent Child bar function '''</p> <pre><code> 为了在这里解释,我们直接继承父类的bar方法,但我们不需要它的方法内容,所以我们使用Super()来修改它。最后,我们也看到了效果。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>To explain here, we directly inherit the bar method of the parent class, but we don't need its method content, so we use super () to modify it. In the end, we also saw the effect.</font>*</details> 注意:如果我们在__init__()里面写入: ;gutter:true;
FooParent.bar.__init__(self)

也就是说,父类的所有方法都需要重构,假设我们没有重构,那么继承父类,如果在这里重构需要与父类的名称相同。

[En]

That is, all the methods of the parent class need to be refactored, assuming that we do not have refactoring, then inherit the parent class, if refactored here needs to be the same as the name of the parent class.

9, Python3的 int 类型详解(为什么int不存在溢出问题?)

参考地址:https://www.cnblogs.com/ChangAn223/p/11495690.html

在Python内部对整数的处理分为普通整数和长整数,普通整数长度是机器位长,通常都是 32 位,超过这个范围的整数就自动当长整数处理,而长整数的范围几乎没有限制,所以long类型运算内部使用大数字算法实现,可以做到无长度限制。

在以前的 python2中,整型分为 int 和 Long,也就是整型和长整型,长整型不存在溢出问题,即可以存放任意大小的数值,理论上支持无线大数字。因此在Python3中,统一使用长整型,用 int 表示,在Python3中不存在 long,只有 int。

长整形 int 结构其实也很简单,在 longinterpr.h 中定义:

csharp;gutter:true;
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};

ob_digit 是一个数组指针,digit可认为是 Int 的别名。

python中整型结构中的数组,每个元素最大存储 15位的二进制数(不同位数操作系统有差异 32 位系统存 16位,64位系统是 32位)。

如 64位系统最大存储32位的二进制数,即存储的最大十进制数为 2^31-1 = 2147483647,也就是说上面例子中数组的一个元素存储的最大值是 2147483647。

需要注意的是:实际存储的是二进制,而不是我们写的十进制

[En]

It should be noted that: * the actual storage is in binary form, not the decimal system we wrote * .

有人说,数组元素所需的内存量是4字节或32位,但实际上用于存储数字的有效位是30(在64位系统中)。原因是指数运算要求位移是5的倍数,这可能是某种优化算法。

[En]

Some people say that the amount of memory required for an array element is 4 bytes or 32 bits, but in fact the significant bits for storing numbers are 30 (in 64-bit systems). The reason is that the displacement is required to be a multiple of 5 in the exponential operation, which may be some kind of optimization algorithm.

10,为什么Python的Range要设计成左开右闭?

Python的Range是左开右闭的,而且除了Python的Range,还有各种语言也有类似的设计。关于Range为什么要设计这个问题,Edsger W.Dijkstra在1982年写过一篇短文中分析了一下其中的原因,当然那时候没有Python,E.W.Dijkstra当年以其他语言为例,但是思路是相通的,这里做摘抄和翻译如下:

为了表示2,3,…,12这样一个序列,有四种方法

  • 2 ≤ i < 13(左闭右开区间)
  • 1 < i ≤ 12(左开右闭区间)
  • 2 ≤ i ≤ 12(闭区间)
  • 1 < i < 13(开区间)

有没有最好的代表作?是的,前两个表示的两端之间的差异恰好是序列的长度,并且在这两个表示中的任一个中,相邻的两个子序列中的一个的上界是另一个的下界,这只允许我们跳过前两个,而不是选择前两个中的最好的,让我们继续分析。

[En]

Is there any of the best representations? Yes, the difference between the two ends of the first two representations happens to be the length of the sequence, and in either of these two representations, the upper bound of one of the two adjacent subsequences is the lower bound of the other, which only allows us to jump out of the first two, not to choose the best of the first two, let’s continue with the analysis.

请注意,当我们取下界时,自然数有一个最小值

[En]

Notice that natural numbers have a minimum, when we take the lower bound

Original: https://www.cnblogs.com/wj-1314/p/12027579.html
Author: 战争热诚
Title: python笔试题(3)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/7445/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

最近整理资源【免费获取】:   👉 程序员最新必读书单  | 👏 互联网各方向面试题下载 | ✌️计算机核心资源汇总