Python是一个基于C语言实现的解释型高级语言, 提供了很多舒适的功能特性,使用起来非常方便。但有的时候, Python的输出结果,让我们感觉一头雾水,其中原因自然是Python语言内部实现导致的,下面我们就给大家总结一些难以理解和反人类直觉的例子。

奇妙的字符串普通相同字符a = 'small_tom'

id(a)

# 输出:140232182302576b = 'small' + '_' + 'tom'

id(b)

# 输出:140232182302576id(a) == id(b)

# 输出:True包含特殊字符a = 'tom'

b = 'tom'

a is b

# 输出:Truea = 'tom!'

b = 'tom!'

a is b

# 输出:Falsea, b = 'tom!', 'tom!'

a is b

# 输出:False Python3.7以下为True'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'

# 输出:True

'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'

# 输出:True Python3.7以下为Falsea = 'tom'

b = ''.join(['t', 'o', 'm'])

a is b

# 输出:

为什么会出现以上的现象呢?因为编译器的优化特性(很多语言的不同编译器都有相应的优化策略),对于不可变对象,在某些情况下并不会创建新的对象,而是会尝试使用已存在的对象,从而节省内存,可以称之为**字符串驻留**。字符串的驻留是隐式的,不受我们控制,但是我们可以根据一些规律来猜测是否发生字符串驻留:所有长度为 0 和长度为 1 的字符串都被驻留

字符串中只包含字母,数字或下划线时将会驻留。所以 'tom!' 由于包含 ! 而未被驻留。

'tom'将被驻留,而''.join(['t', 'o', 'm'])不被驻留

当在同一行将 a 和 b 的值设置为 "tom!" 的时候, Python 解释器会创建一个新对象, 然后同时引用第二个变量(译: 仅适用于3.7以下). 如果你在不同的行上进行赋值操作, 它就不会“知道”已经有一个 wtf!对象 (因为 "wtf!" 不是按照上面提到的方式被隐式驻留的). 它是一种编译器优化, 特别适用于交互式环境

当在同一行将 a 和 b 的值设置为 "tom!" 的时候, Python 解释器会创建一个新对象, 然后同时引用第二个变量(仅适用于3.7以下). 如果你在不同的行上进行赋值操作, 它就不会“知道”已经有一个 tom!对象 (因为 "tom!" 不是按照上面提到的方式被隐式驻留的). 它是一种编译器优化, 特别适用于交互式环境.

常量折叠(constant folding) 是 Python 中的一种 窥孔优化(peephole optimization) 技术. 这意味着在编译时表达式 'a'*20 会被替换为 'aaaaaaaaaaaaaaaaaaaa' 以减少运行时的时钟周期. 只有长度小于 20 的字符串才会发生常量折叠. 为什么呢?想象一下由于表达式 'a'*10**10 而生成的.pyc 文件的大小)。

**PS**:如果是在Python3.7中会发现部分执行结果会不一样,因为3.7版本中常量折叠已经从窥孔优化器迁移至新的AST优化器,后者可以以更高的一致性来执行优化。但是在3.8中结果又不一样了,他们都是用了AST优化器,可能是3.8中有一些其他的调整。

字典的魔法some_dict = {}

some_dict[5.5] = "Ruby"

some_dict[5.0] = "JavaScript"

some_dict[5] = "Python"some_dict[5.5]

# 输出:Ruby

some_dict[5.0]

# 输出:Python

some_dict[5]

# 输出:PythonPython字典通过检查键值是否相等和比较哈希值来确定两个键是否相同

具有相同值的不可变对象在Python中始终具有相同的哈希值

虽然5.0和5好像是不一样,但实际上是一样的,在python中是不存在整型和浮点型的,只有一个数值型5 == 5.0

# 输出:True

hash(5) == hash(5.0)

# 输出:True

注意: 具有不同值的对象也可能具有相同的哈希值(哈希冲突)当执行 some_dict[5] = "Python" 语句时, 因为Python将5和5.0识别为some_dict 的同一个键, 所以已有值 "JavaScript" 就被 "Python" 覆盖了.

到处都返回def some_func():

try:

return 'from_try'

finally:

return 'from_finally'

some_func()

# 始终输出:from_finally

这是一个非常严重的问题,而且也非常常见,也很长用到,需要格外的注意。在异常捕获的时候,我们经常会用到finally来执行异常捕获后必须执行的处理。但是return在很多语言当中表示跳出当前的执行模块,但是在这里就有些颠覆我们的认知了,所以必须重点关注。当在 "try...finally" 语句的 try 中执行 return, break 或 continue 后, finally 子句依然会执行.

函数的返回值由最后执行的 return 语句决定. 由于 finally 子句一定会执行, 所以 finally 子句中的 return 将始终是最后执行的语句

出人意料的is

下面是一个在网上非常有名的例子.a = 256

b = 256

a is b

# 输出:True

a = 257

b = 257

a is b

# 输出:False

a = 257; b = 257

a is b

# 输出:True

a, b = 257, 257

a is b

# 输出:True

1.我们要说一下is和==的区别is 运算符检查两个运算对象是否引用自同一对象 (即, 它检查两个运算对象地址是否相同)

==运算符比较两个运算对象的值是否相等a = 257

b = 257

a is b

# 输出:False

a == b

# 输出:True

2.为什么256和257的结果不一样?

当你启动Python的时候, 数值为-5到256 的对象就已经被分配好了. 这些数字因为经常被使用, 所以会被提前准备好。Python通过这种创建小整数池的方式来避免小整数频繁的申请和销毁内存空间,从而造成内存泄漏和碎片。

3.当a和b在同一行中使用相同的值初始化时,会指向同一个对象.a, b = 257, 257

id(a)

# 输出:4391026960

id(b)

# 输出:4391026960

a = 257

b = 257

id(a)

# 输出:140232163575152

id(b)

# 输出:140232163574768当 a 和 b 在同一行中被设置为 257 时, Python 解释器会创建一个新对象, 然后同时引用第二个变量. 如果你在不同的行上进行, 它就不会 "知道" 已经存在一个 257 对象

必须要注意的是这是一种特别为交互式环境做的编译器优化. 当你在实时解释器中输入两行的时候, 他们会单独编译, 因此也会单独进行优化. 如果你在 .py 文件中尝试这个例子, 则不会看到相同的行为, 因为文件是一次性编译的,如果是运行py文件将得到不同的结果

test.pya, b = 257, 257

print(id(a))

print(id(b))

# 输出:

列表复制row = [""]*3

# 并创建一个变量board

board = [row]*3

print(row)

print(board)

# 输出:['', '', '']

# 输出:[['', '', ''], ['', '', ''], ['', '', '']]

board[0][0] = 'X'

print(board)

# 输出:[['X', '', ''], ['X', '', ''], ['X', '', '']]当我们初始化 row 变量时, 下面这张图展示了内存中的情况。而当通过对 row 做乘法来初始化 board 时, 内存中的情况则如下图所示 (每个元素 board[0], board[1] 和 board[2] 都和 row 一样引用了同一列表.)我们可以通过不使用变量 row 生成 board 来避免这种情况board = [['']*3 for _ in range(3)]

board[0][0] = "X"

board

# 输出:[['X', '', ''], ['', '', ''], ['', '', '']]

这样就会创建三个[''] * 3,而不是把[''] * 3标记三次

闭包funcs = []

results = []

for x in range(7):

def some_func():

return x

funcs.append(some_func)

results.append(some_func()) # 注意这里函数被执行了

funcs_results = [func() for func in funcs]

print(results)

print(funcs_results)

# 输出:[0, 1, 2, 3, 4, 5, 6]

# 输出:[6, 6, 6, 6, 6, 6, 6]

即使每次在迭代中some_func中的x值都不相同,所有的函数还是都返回6.powers_of_x = [lambda x: x**i for i in range(10)]

[f(2) for f in powers_of_x]

# 输出:[512, 512, 512, 512, 512, 512, 512, 512, 512, 512]当在循环内部定义一个函数时, 如果该函数在其主体中使用了循环变量, 则闭包函数将与循环变量绑定, 而不是它的值. 因此, 所有的函数都是使用最后分配给变量的值来进行计算的

可以通过将循环变量作为命名变量传递给函数来获得预期的结果. 为什么这样可行? 因为这会在函数内再次定义一个局部变量funcs = []

for x in range(7):

def some_func(x=x):

return x

funcs.append(some_func)

funcs_results = [func() for func in funcs]

print(funcs_results)

# 输出:[0, 1, 2, 3, 4, 5, 6]

is not ... 不是 is (not ...)'something' is not None

# 输出:True

'something' is (not None)

# 输出:Falseis not 是个单独的二元运算符, 与分别使用 is 和 not 不同.

如果操作符两侧的变量指向同一个对象, 则 is not 的结果为 False, 否则结果为 True.

不存在的零点from datetime import datetime

midnight = datetime(2018, 1, 1, 0, 0)

midnight_time = midnight.time()

noon = datetime(2018, 1, 1, 12, 0)

noon_time = noon.time()

if midnight_time:

print("Time at midnight is", midnight_time)

if noon_time:

print("Time at noon is", noon_time)

# 输出:Time at midnight is 00:00:00

# 输出:Time at noon is 12:00:00

以上代码如果是在python3.5之前的版本,只会输出Time at noon is 12:00:00,在Python 3.5之前, 如果 datetime.time 对象存储的UTC的午夜时间(译: 就是 00:00), 那么它的布尔值会被认为是 False. 当使用 if obj: 语句来检查 obj 是否为 null 或者某些“空”值的时候, 很容易出错.

类属性和实例属性class A:

x = 1

class B(A):

pass

class C(A):

pass

print(A.x, B.x, C.x)

# 输出:1 1 1

B.x = 2

print(A.x, B.x, C.x)

# 输出:1 2 1

A.x = 3

print(A.x, B.x, C.x)

# 输出:3 2 3

a = A()

print(a.x, A.x)

# 输出:3 3

a.x += 1

print(a.x, A.x)

# 输出:4 3class SomeClass:

some_var = 15

some_list = [5]

another_list = [5]

def __init__(self, x):

self.some_var = x + 1

self.some_list = self.some_list + [x]

self.another_list += [x]

some_obj = SomeClass(420)

print(some_obj.some_list)

print(some_obj.another_list)

another_obj = SomeClass(111)

print(another_obj.some_list)

print(another_obj.another_list)

print(another_obj.another_list is SomeClass.another_list)

print(another_obj.another_list is some_obj.another_list)类变量和实例变量在内部是通过类对象的字典来处理. 如果在当前类的字典中找不到的话就去它的父类中寻找

+= 运算符会在原地修改可变对象, 而不是创建新对象. 因此, 在这种情况下, 修改一个实例的属性会影响其他实例和类属性.

从有到无some_list = [1, 2, 3]

some_dict = {

"key_1": 1,

"key_2": 2,

"key_3": 3

}

some_list = some_list.append(4)

some_dict = some_dict.update({"key_4": 4})

print(some_list)

print(some_dict)

# 输出:None

# 输出:None

不知道有没有人能一眼看出问题所在,这是一个写法错误,并不是特殊用法。因为列表和字典的操作函数,比如list.append、list.extend、dict.update等都是原地修改变量,不创建也不返还新的变量

子类继承关系from collections import Hashable

print(issubclass(list, object))

print(issubclass(object, Hashable))

print(issubclass(list, Hashable))

# 输出:True

# 输出:True

# 输出:False

子类关系是可以传递的,A是B的子类,B是C的子类,那么A应该也是C的子类,但是在python中就不一定了,因为在python中使用__subclasscheck__函数进行判断,而任何人都可以定义自己的__subclasscheck__函数当 issubclass(cls, Hashable) 被调用时, 它只是在 cls 中寻找 __hash__ 方法或者从继承的父类中寻找 __hash__ 方法.

由于 object is 可散列的(hashable), 但是 list 是不可散列的, 所以它打破了这种传递关系class MyMetaClass(type):

def __subclasscheck__(cls, subclass):

print("Whateva, I do what I want!")

import random

return random.choice([True, False])

class MyClass(metaclass=MyMetaClass):

pass

print(issubclass(list, MyClass))

# 输出:Whateva, I do what I want!

# 输出:True 或者 False 因为是随机取的

元类在python中是比较深入的知识点,后面我们有时间再讲

斗转星移import numpy as np

def energy_send(x):

# 初始化一个 numpy 数组

np.array([float(x)])

def energy_receive():

# 返回一个空的 numpy 数组

return np.empty((), dtype=np.float).tolist()

energy_send(123.456)

print(energy_receive())

# 输出:123.456

这到底是无中生有还是斗转星移呢?energy_receive函数我们返回了一个空的对象,但是结果是上一个数组的值,为什么呢?在energy_send函数中创建的numpy数组并没有返回, 因此内存空间被释放并可以被重新分配.

numpy.empty()直接返回下一段空闲内存,而不重新初始化. 而这个内存点恰好就是刚刚释放的那个但是这并不是绝对的.

python工程师薪资坑吗-Python中反人类直觉的特性,你踩过坑吗?相关推荐

  1. 2019年招聘python工程师薪资飙升,Python这些技能你掌握了吗

    Python的火热,也带动了工程师们的就业热.那么,Python的市场需求和工程师待遇到底如何呢?今天我们来看看具体数据. 2019年招聘python工程师薪资飙升,Python这些技能你掌握了吗?P ...

  2. 为什么python工程师掌握这些就够了_Python工程师薪资飙升,Python这些技能你掌握了吗...

    Python的火热,也带动了工程师们的就业热.那么,Python的市场需求和工程师待遇到底如何呢?今天我们来看看具体数据.2019年招聘python工程师薪资飙升,Python这些技能你掌握了吗?Py ...

  3. python全栈工程师薪水_不止 20K,Python 工程师薪资再飙升(内附转型指南)

    原标题:不止 20K,Python 工程师薪资再飙升(内附转型指南) Python 诞生之初就被誉为最容易上手的编程语言.进入火热的 AI 人工智能时代后,它也逐渐取代 Java,成为编程界的头牌语言 ...

  4. python工程师需要什么学历,python工程师怎么考证

    python认证证书有哪些 python认证证书有很多,比如常见的证书有:全国计算机等级考试Python程序语言设计.Python Institute资格认证初级PCEP证书.Python Insti ...

  5. python工程师薪资坑吗-入坑Python爬虫值不值?

    点击蓝字"python教程"关注我们哟! 有很多小伙伴,学习了Python的很多知识,比如基础语法.函数,web.爬虫.人工智能等,但是这么多就业方向,一时难选,不知道入坑Pyth ...

  6. python工程师薪资坑吗-python能到什么程度

    对于那些打算入坑Python的小伙伴,想必会有疑问,Python可以为你带来高收入吗? 这可不一定的.就好比大家都说炒股可以发家致富,但真正可以发家致富的有多少?有人说,创业就可以比肩李嘉诚,但真正创 ...

  7. python工程师薪资坑吗-6年Python开发工程师精心总结学习思路,再不看看就凉了...

    无论是实用性还是易用性,Python 都是学习编程最具性价比的选择. 如果你学过其他的语言,再学 Python 将会让你的技能树大大地拓宽,有能力涉足更多的领域. 学习编程从入门到放弃的人不计其数,很 ...

  8. python工程师工资多少-2018年涨工资了吗?Python 工程师薪资最新出炉

    著名的TIOBE 刚刚发布了6 月编程语言排行榜.TIOBE编程社区索引是编程言语评价的一个指标,该指数每月更新一次.小伙伴们赶忙看看下面的排名情况吧! 图1 TIOBE排行榜是根据互联网上有经验的程 ...

  9. 2018年python工作好找吗-2018年涨工资了吗?Python 工程师薪资最新出炉

    著名的TIOBE 刚刚发布了6 月编程语言排行榜.TIOBE编程社区索引是编程言语评价的一个指标,该指数每月更新一次.小伙伴们赶忙看看下面的排名情况吧! 图1 TIOBE排行榜是根据互联网上有经验的程 ...

最新文章

  1. Android期末项目-校园论坛
  2. SQL CONVERT在日期转换中的使用示例
  3. 模拟简单计算器及比较大小
  4. 阿里研发支出连续三年登顶; 《MIT科技评论》为阿里AI语音技术点赞;达摩院引入以色列著名计算机视觉科学家 | 周博通...
  5. new uk psw policy
  6. Angular platform-server.js 里动态创建 JavaScript 标签页的场景
  7. 【东软实训】SQL多表链接
  8. vue 文件转换二进制_在vue中使用axios实现post方式获取二进制流下载文件(实例代码)...
  9. nginx rewrite php参数,Nginx rewrite伪静态配置参数详解
  10. ajax框架怎么学,Ajax框架之DWR学习(文件上传案例)
  11. linux小红帽实验心得,小红帽心得体会.doc
  12. 人工智能之语音识别技术【科普】
  13. 中国人上淘宝喜欢干啥 你知道吗?
  14. 在中琅条码打印软件中怎样实现CMYK的设置
  15. HTML页面如何引入其他HTML页面
  16. Kroll任命Richard Davies担任网络风险业务副董事总经理,常驻香港
  17. linux主进程退出时,结束子进程
  18. Investigating Typed Syntactic Dependencies for Targeted Sentiment Classification Using GAT(2020)
  19. 薇诺娜如何以创新战略在新消费浪潮中屹立不倒
  20. 知识图谱数据管理:存储与检索

热门文章

  1. 关于计算机中 原码, 反码, 补码 详解
  2. Hdu 1217 最短路.cpp
  3. hbase coprocessor的分析
  4. LNK2001连接错误
  5. nchar,char,varchar与nvarchar区别(收藏)
  6. RookeyFrame2.0发布,UI重构
  7. WPF Geometry 添加Path数据
  8. redis-cluster配置
  9. Mybatis 针对ORACLE和MYSQL的批量插入与多参数批量删除
  10. Objective-C语法之代码块(block)的使用