查看原文
其他

WTF Python:开启懵逼模式第二弹

2017-10-09 大邓 大邓带你玩python

wtf-10 循环内的函数

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]

按照我们的猜想,funcs_results结果应该是[0, 1, 2, 3, 4, 5, 6],不应该全是6。

分析:

在循环体中函数使用的是循环体中的变量,对于每一次循环而言,函数绑定的是变量而不是变量的值。所以在程序运行时,所有的函数都使用的变量最新赋值。

怎么实现我们的想法,让每次循环发生变化?

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]

wtf-11 循环变量超出作用域

Loop variables leaking out of local scope!翻译不知道对不对

for x in range(7):    if x == 6:        print(x, ': for x inside loop')        
#但是x没有在循环体外定义,怎么x变成全局变量了?        
print(x, ': x in global')
6 : for x inside loop 6 : x in global# 定义一个全局变量x
x = -1
for x in range(7):    if x == 6:        print(x, ': for x inside loop')        
#还是不懂为啥出现这情况
print(x, ': x in global')
6 : for x inside loop 6 : x in global

在Python中,for循环使用它们存在的范围,并在结束循环时留下循环变量。如果我们在全局命名空间中前显式地定义for循环变量,这些循环变量就会得到调用。在这种情况下,它会重新绑定现有的变。

有点类似wtf10

x = 1
print([x for x in range(5)])
print(x, ': x in global')
[0, 1, 2, 3, 4] 1 : x in global

wtf-12 小心默认的可变对象

def some_func(default_arg=[]):    default_arg.append("some_string")    return default_arg

print(some_func())
print(some_func())
print(some_func())
['some_string'] ['some_string', 'some_string'] ['some_string', 'some_string', 'some_string']

分析:

Python中函数的默认可变参数在每次调用该函数时都不会被初始化。 而是使用最近给它们分配的值作为默认值。 

当我们明确地将[]传递给somefunc作为参数时,defaultarg变量的默认值不被使用,因此该函数没有按预期方式返回。

#改成这样就可以如您所愿
def some_func(default_arg):    default_arg.append("some_string")    return default_arg

print(some_func([]))
print(some_func([]))
print(some_func([]))
['some_string'] ['some_string'] ['some_string']#也可以在函数内加判断,初始化
def some_func(default_arg=None):    if not default_arg:        default_arg = []    default_arg.append("some_string")    return default_arg

wtf-13 近似的形式却截然不同

a = [1, 2, 3, 4]
b = a a = a + [5, 6, 7, 8]

print(a)
print(b)
[1, 2, 3, 4, 5, 6, 7, 8] [1, 2, 3, 4]

分析:

a = [1,2,3,4],a拥有一块内存地址。

b = a,使得b也绑定跟a一样的内存地址,也就是此时a与b是内存地址与值都相同。

a = a + [5,6,7,8]。此时相当于新建了一个变量a,解除了原来a的内存地址。此时a与b不相同了

a = [1, 2, 3, 4]
b = a a += [5, 6, 7, 8]

print(a)
print(b)
[1, 2, 3, 4, 5, 6, 7, 8] [1, 2, 3, 4, 5, 6, 7, 8]

分析

a = [1,2,3,4],a拥有一块内存地址。

b = a,使得b跟着a,拥有一样的内存地址,也就是此时a与b是内存地址与值都相同。

a += [5,6,7,8]。此时原地对内存进行修改,内存地址没变,数据发生变化。b也跟着a一起变

wtf-14 修改不可变型对象

#按照我们的理解,元组一旦创建就不可修改。
some_tuple = ("A", "tuple", "with", "values")
another_tuple = ([1, 2], [3, 4], [5, 6])
some_tuple[2] = "change this"--------------------------------------------------------------------------- TypeError                                 Traceback (most recent call last) <ipython-input-57-a46a5b5ae67c> in <module>() ----> 1 some_tuple[2] = "change this" TypeError: 'tuple' object does not support item assignment#对不可对象内的可变对象的引用,此时修改不会报错
another_tuple[2].append(1000)
([1, 2], [3, 4], [5, 6, 1000])#不可变对象another_tuple引用,会报错 ,但修改已发生
another_tuple[2] += [99, 999]
--------------------------------------------------------------------------- TypeError                                 Traceback (most recent call last) <ipython-input-59-d07c65f24a63> in <module>() ----> 1 another_tuple[2] += [99, 999] TypeError: 'tuple' object does not support item assignment#虽然上一行报错,但实际上发生变化了another_tuple([1, 2], [3, 4], [5, 6, 1000, 99, 999])

总结:

不可变序列类型的对象一旦创建就不能改变。 (如果对象包含对其他对象的引用,则这些其他对象可能是可变,此时不可变序列可能被修改;但是,由不可变对象直接引用的对象的集合不能更改。)

wtf-15 作用域未定义变量

a = 1

def some_func():    return a

def another_func():    a += 1    return a

print(some_func())
1print(another_func())--------------------------------------------------------------------------- UnboundLocalError                         Traceback (most recent call last) <ipython-input-64-3b85aab5eec8> in <module>() ----> 1 print(another_func()) <ipython-input-63-c6c17295dd3e> in another_func()      4      5 def another_func(): ----> 6     a += 1      7     return a      8 UnboundLocalError: local variable 'a' referenced before assignment

分析:

当对作用域中的变量进行赋值操作时,该变量变为局部变量。所以a变成了anotherfunc的局部变量,anotherfunc函数没有给局域变量a初始化,所以调用函数时会报错。

#修正方法,作用域内初始化,声明a为全局变量a
def another_func():    global a    a += 1    return a

print(another_func())
2

wtf-16 到处返回return

def some_func():    try:        return 'from_try'    finally:        return 'from_finally'    
print(some_func())
from_finally

分析:

当break、return、continue在try...finally..对语句中的try语句中执行时,finally语句中的也会被执行的。

函数的返回值由执行的最后一个返回语句确定。 由于finally子句总是执行,所以在finally子句中执行的return语句将永远是执行的最后一个。

wtf-17 Python中逻辑运算律

print('True is False == False:',True is False == False)
print('False is False is False:',False is False is False)
print('1 > 0 < 1',1 > 0 < 1)
print('(1 > 0) < 1',(1 > 0) < 1)
print('1 > (0 < 1)',1 > (0 < 1))
True is False == False: False False is False is False: True 1 > 0 < 1 True (1 > 0) < 1 False 1 > (0 < 1) False

分析:

False is False is False 相当于 (False is False) and (False is False)

True is False == False 相当于 True is False and False == False ,因为第一部分 (True is False) 相当于False, 第二部分(False == False)相当于True,总体结合起来(False and True)是False.

1 > 0 < 1 相当于 1 > 0 and 0 < 1 ,结果True.

其他的类似,都是很像数学里的逻辑运算律。

x = 5

class SomeClass:    x = 17    y = (x for i in range(10))

print(list(SomeClass.y)[0])
5x = 5
class SomeClass:    x = 17    y = [x for i in range(10)]

print(SomeClass.y[0])
5

分析:

1、列表和生成器都有自己的作用域

2、嵌套在类内的列表或生成器中的变量忽略类内绑定的变量。如在本例子中的 推导式中的x忽略类内绑定的x。

wtf-18 奇葩的原地修改对象

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,dict.update,list.sort等)的方法会在原位修改对象并返回None。 其背后的理由是如果可以在原地进行操作,则避免制作对象的副本(新建内存空间)来提高性能

wtf-19 类属性与实例属性

class A:    x = 1

class B(A):    pass

class
C(A):    pass
print(A.x, B.x, C.x)1 1 1#相当于B新建了xB.x = 2
print(A.x, B.x, C.x)
1 2 1#A新建了x,因为C继承A。所以A、C一样。#但因为B自己独立新建了,已经摆脱了A
A.x = 3
print(A.x, B.x, C.x)
3 2 3#实例化A,返回的x一样的
a = A()
print(a.x, A.x)
3 3#+=操作,相当于a原地修改实例a。
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)
some_obj.some_list
[5, 420]some_obj.another_list[5, 420]another_obj = SomeClass(111)another_obj.some_list[5, 111]another_obj.another_list[5, 420, 111]print(id(some_obj),id(another_obj))
print(id(some_obj.some_list),id(another_obj.some_list))

#but这里却一样
print(id(some_obj.another_list),id(another_obj.another_list))
4371905224 4371906512 4371728648 4371728520 4371874696 4371874696

分析:

  1. 类的属性和实例的属性是储存在类字典中,当当前类字典中没有这个属性,那么就要找该类的父类。

  2. +=相当于原地更改变量,并没有新建对象。所以改变一个实例的属性时,会影响到另一个实例的属性;


历史文章:

数据采集

selenium驱动器配置详解

抓取单博主的所有微博及其评论

爬虫神器PyQuery的使用方法

pyquery一些自定义的用法

【视频】猫途鹰网站评论数据抓取

【视频讲解】在scrapy框架中如何使用selenium?

【实战视频】使用scrapy写爬虫-爬知乎live

简易SQLite3数据库学习

【视频教程】对视频进行基本操作

【视频】抓包小练习-B站评论数据抓取

fake-useragent库:值得花2分钟学习的库

【视频】爬取饿了么平台上的门店信息

如何抓取视频资源-以头条视频为例

当爬虫遭遇验证码,怎么办

【视频】手把手教你抓美女~

【视频】有了selenium,小白也可以自豪的说:“去TMD的抓包、cookie”

【视频】快来get新技能--抓包+cookie,爬微博不再是梦

【视频教程】用python批量抓取简书用户信息

识别假货有绝招,就用python爬淘宝评论

用Python抓取百度地图里的店名,地址和联系方式

文本处理分析

gensim:用Word2Vec进行文本分析

Python NLP中的五大英雄

用SenticNet库做细粒度情感分析

如何对csv文件中的文本数据进行分析处理

复杂网络分析数据准备篇

文本分析之网络关系

用pyecharts制作词云图

用词云图解读“于欢案”

基于共现发现人物关系的python实现

图片数据处理

OpenCV:快速入门图片人脸识别

好玩的OpenCV:图片操作的基本知识(1)

好玩的OpenCV:图像操作的基本知识(2)

OpenCV:计算图片有多色

如何对数据进行各种排序?

其他

【视频】初学者必看:python环境配置

开扒皮自己微信的秘密

初识装饰器函数

计算运行时间-装饰器实现

花十分钟,给爱机安装个MongoDB

chardet库:轻松识别文件的编码格式

使用Python登录QQ邮箱发送QQ邮件

WTF Python: 开启你的懵逼模式

8行代码实现微信聊天机器人






您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存