Python陷阱

本文整理了一些平常使用python时碰到的陷阱或错误

全局变量v.s.局部变量

global定义位置

global a
a=1
def func():
    a+=1
    print(a)

func()
a+=1
print(a)

File "XXX", line 9, in func()
File "XXX", line 6, in func a+=1
UnboundLocalError: local variable 'a' referenced before assignment

原因:当已经在函数体外的变量已经被定义为全局变量后,在函数体内又被重新赋值一遍,这时的变量在函数体内又被定义为局部变量,只在函数体内才会有效。在函数体外的时候恢复之前未在函数体内定义的状态。1

修改

a=1
def func():
    global a
    a+=1
    print(a)

func()
a+=1
print(a)
输出: 2 3 对比

global a
a=1
def func():
    b=a+5
    print(b)

func()
a+=1
print(a)
输出: 6 2

 

跨模块使用global

跨模块使用global会更容易带来复杂且难以理解的陷阱。该节参考的是punchagan的博文2以及对应的译文3

• Python 的全局变量是模块 (module) 级别的
• 每个python函数拥有对应的__globals__字典,该字典与函数所属模块的__dict__字典完全相同。函数的全局变量也会从这个字典中获取。
• 避免全局变量将使得程序更容易被调试,同时也能提升程序的可读性
20190819003250.png


模块1--foo.py|模块2--bar.py
-|-

def f():
  print(a)
def main():
  global a
  a = 5
  f()
 main()

|

from foo import f
 def main():
  global a
  a=4
  f()
 main()


运行--foo.py|运行--bar.py
输出正常结果5|报错:NameError

File "XXX", line 13, in main()
File "XXX", line 9, in main f()
File "XXX", line 5, in f print(a)
NameError: global name 'a' is not defined

这是因为a被定义在foo.pymain()函数中,而当导入f()函数时,foo.pymain函数并未被运行,所以a也没有被定义。

反汇编f()

import dis
from foo import f

dis.dis(f)
输出:(f()函数的字节码) 行号|-|- -|-|- 2|0 LOAD_GLOBAL|0 (print) -|2 LOAD_GLOBAL | 1 (a) -|4 CALL_FUNCTION | 1 -|6 POP_TOP|- -|8 LOAD_CONST | 0 (None) -|10 RETURN_VALUE|-

从反汇编可以看出变量a被认为是全局变量。Python中的每一个函数都拥有一个__globals__字典变量,该变量实际是函数所属模块的__dict__变量的引用。所以在bar.py中我们想在bar.main函数中将全局变量a赋值为4,实际改变的是bar.py__dict__字典变量 (注:而不是定义ffoo.py__dict__字典变量)

mutable对象

用mutable对象作默认参数

本节参考博文:程序员必知的Python陷阱与缺陷列表4。Python和其他很多语言一样,提供了默认参数,默认参数确实是个好东西,可以让函数调用者忽略一些细节(比如GUI编程,Tkinter,QT),对于lambda表达式也非常有用。但是如果使用了可变对象作为默认参数,那么事情就不那么愉快了。
这个问题是我在做LeetCode的TwoSum时候发现的。我采用一遍哈希表+尾递归的解法,发现总是思路没问题但结果一直出错(具体解题思路可看我的另一篇博文Two Sum):

One-pass Hash Table + tail recursion


class Solution(object):
    def twoSum(self, nums, target,i=0,dict={}):
        if dict.get(target-nums[i]) is not None:
            return dict.get(target - nums[i]),i
        else:
            dict[nums[i]] = i
            i+=1
            return self.twoSum(nums,target,i,dict)
if __name__ == '__main__':
    nums = [3,3]
    target = 6
    sol=Solution()
    ans=sol.twoSum(nums,target)
    print(ans)
    ans2=sol.twoSum(nums,target)
    print(ans2)

输出:
[0,1]
[0,0]

一开始以为是形参和实参的问题,后来男票提醒才知道是mutable对象的问题。python中一切都是对象,函数也不列外,默认参数只是函数的一个属性。而默认参数在函数定义的时候已经赋值了。

Default parameter values are evaluated when the function definition is executed.

stackoverflow上有一个更适当的例子来说明默认参数是在定义的时候赋值,而不是调用的时候。

stackoverflow example


import time
def report(when=time.time()):
    return when
report()
report()

输出:

1500113234.487932
1500113234.487932

python docoment 给出了标准的解决办法:

A way around this is to use None as the default, and explicitly test for it in the body of the function

mutable object solution

import time
def report(when=None):
    if when is None:
        when = time.time()
    return when
report()
report()
输出:
1500113234.487932 1500113448.552873

 

mutable对象和(x+=y v.s. x=x+y)

待更新。。。

Python的float除法和整除法

不同版本的python对整数/整数的处理不同:
教老版本的python:整数/整数---整除法
较新版本的python:整数/整数---float除法

最保险的做法应明确告诉编译器你想要的结果,即:
整除法:用//,e.g.数//数
float除法:用/且除数与被除数中有一个数是浮点数,e.g.float/数数/float

参考


  1. Python中局部变量与全局变量的解释

  2. Python's globals

  3. Python's globals译文

  4. 程序员必知的Python陷阱与缺陷列表

转载于:https://www.cnblogs.com/weixia14/p/11374754.html

Python陷阱汇总 引言     Python虽然没有C/C++的指针和各种数据类型转换,但不代表它没有一片坦途,对于初学者,再感叹Python的简单和强大之时,可能一不小心就掉到陷阱中去了。为了给后来者警示,特总结Python的各种陷阱,以起到“前车之覆,后车之鉴”的效果。 阅读详情

相关推荐

网址收藏 网址收藏 网址收藏

绝对能看能用的C#代码 using System; using System.Collections.Generic; using System.Text; using System.Collections; namespace NetAddressCollector { public class CDataAccess : IDataAccess { #region IDataAccess 成员 private ArrayList _classList; public ArrayList ClassList { get { return _classList; } } private ArrayList _addressList; public ArrayList AddressList { get { return _addressList; } } private bool _isDirty = false; public bool IsDirty { get { return _isDirty; } } public void LoadData() { _classList = new ArrayList(); _addressList = new ArrayList(); string content = CFileOperation.ReadFile(CConst.DATAFILE); if (content.Length < 1) return; string[] ss1 = content.Split(new string[] { CConst.SPLITOR_CLASS_HTTP }, StringSplitOptions.RemoveEmptyEntries); string classString = ss1[0]; string httpString = ss1[1]; string[] ss2 = classString.Split(new string[] { CConst.SPLITOR_CLASS_ITEM }, StringSplitOptions.RemoveEmptyEntries); foreach (string s in ss2) _classList.Add(new CClass(s)); string[] ss3 = httpString.Split(new string[] { CConst.SPLITOR_HTTP_ITEM }, StringSplitOptions.RemoveEmptyEntries); foreach (string s in ss3) { string[] ss4 = s.Split(new string[] { CConst.SPLITOR_HTTP_DETAIL }, StringSplitOptions.RemoveEmptyEntries); foreach (CClass c in _classList) { if (c.Name == ss4[0]) { _addressList.Add(new CAddress(c, ss4[1], ss4[2], ss4[3])); break; } } } _isDirty = false; } public void SaveData() { StringBuilder sbClass = new StringBuilder(); foreach (CClass c in _classList) sbClass.Append(string.Format("{0}{1}", CConst.SPLITOR_CLASS_ITEM, c.Name)); StringBuilder sbContent = sbClass.Append(CConst.SPLITOR_CLASS_HTTP); StringBuilder sbHttp = new StringBuilder(); foreach (CAddress http in _addressList) sbHttp.Append(string.Format("{0}{1}", CConst.SPLITOR_HTTP_ITEM, http.ToString())); sbContent.Append(sbHttp); CFileOperation.WriteFile(CConst.DATAFILE, sbContent.ToString()); _isDirty = false; } public bool ExistClass(CClass httpClass) { foreach (CClass c in _classList) { if (c.Name == httpClass.Name) return true; } return false; } public bool ExistHttp(CAddress http) { foreach (CAddress addr in _addressList) { if (addr.Name == http.Name && addr.Name == http.Http) return true; } return false; } public void AddClass(string className) { CClass c = new CClass(className); AddClass(c); } public void AddClass(CClass httpClass) { _classList.Add(httpClass); _isDirty = true; } public void RemoveClass(CClass httpClass) { for (int i = _addressList.Count - 1; i >= 0; i--) { CAddress http = (CAddress)_addressList[i]; if (http.HttpClass.Name == httpClass.Name) RemoveHttp(http); } _classList.Remove(httpClass); _isDirty = true; } public void AddHttp(CClass httpClass, string name, string http, string remark) { CAddress addr = new CAddress(httpClass, name, http, remark); AddHttp(addr); } public void AddHttp(CAddress http) { _addressList.Add(http); _isDirty = true; } public void RemoveHttp(CAddress http) { _addressList.Remove(http); _isDirty = true; } #endregion } }

了解8大Python陷阱,深入理解Python

学习了解python常见的使用陷阱,避免二次踩坑 Python是最流行的且适合初学者学习的语言之一。它的语法非常优雅简洁。只要知道python基础知识,阅读代码几无障碍。 然而,就像其他语言一样,Python确实有一些古怪特殊的地方。本文将介绍Python 8大高频陷阱,告诉你冰山之下的世界! 注意:文章中将只提到与Python3相关的使用陷阱,因为Python2即将废弃。 1. 命名空间和作用域 当站在计算机底层观察Python时,有两件事注意:命名空间和作用域。 命名空间 Python是一种面向.

pythontip的博客 419

8大Python陷阱,深入理解Python

学习了解python常见的使用陷阱,避免二次踩坑 Python是最流行的且适合初学者学习的语言之一。它的语法非常优雅简洁。只要知道python基础知识,阅读代码几无障碍。 然而,就像其他语言一样,Python确实有一些古怪特殊的地方。本文将介绍Python8大高频陷阱,告诉你冰山之下的世界! 注意:文章中将只提到与Python3相关的使用陷阱,因为Python2即将废弃。 1. 命名空间和作用域 当站在计算机底层观察Python时,有两件事注意:命名空间和作用域。 命名空间 Python是一种

SixStar_FL的博客 670

Python开发中常见的坑(持续更新)

Python开发过程中,新手和资深开发者都可能遇到的“坑”.这些坑部分和使用的开发语言特色有深深的联系.在这个文章中,列举一下,哪些坑是较常出现的.欢迎大家在评论中举出没发现的坑,我及时作个补充.

MARST.ZHANG的博客 1247

2024年Python最全十二个坑爹的Python陷阱_python坑人

问题的本质在与python中的属性查找规则,LEGB(local,enclousing,global,bulitin),在上面的例子中,i就是在闭包作用域(enclousing),而Python的闭包是 迟绑定 , 这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的。上面的例子中,6这个元素就没有被删除。我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。

2401_84692299的博客 956

python 陷阱_五个python陷阱

python 陷阱Many posts out there catalog the “gotchas” of Python and/or its most popular packages. This blog is yet another entry in that series, but with one difference: I’ve actually, genuinely made al...

weixin_26705651的博客 661

坑爹的Python陷阱

本文版权归作者xybaby我个人对陷阱的定义是这样的:代码看起来可以工作,但不是以你“想当然“”的方式。如果一段代码直接出错,抛出了异常,我不认为这是陷阱。比如,Pyth...

涛哥聊Python 665

Python传参陷阱

Python传参陷阱 文章目录Python传参陷阱默认传参递归中的参数 默认传参 设计一个Python函数时,免不了用到默认传参;默认传参时,又免不了用到列表类型。如下面这样: def add_elem_to_list(something, list_=[]): for i in something: list_.append(i) 乍看之下,好似没什么问题,我们像下边这...

有关心情 625

2024年Python最新了解8大Python陷阱,深入理解Python_python陷阱是什么意思

Python是最流行的且适合初学者学习的语言之一。它的语法非常优雅简洁。只要知道python基础知识,阅读代码几无障碍。然而,就像其他语言一样,Python确实有一些古怪特殊的地方。本文将介绍Python 8大高频陷阱,告诉你冰山之下的世界!注意:文章中将只提到与Python3相关的使用陷阱,因为Python2即将废弃。

2401_84688353的博客 419

Python闭包与闭包陷阱

Python中的闭包陷阱指的是在闭包中引用了变量时,如果该变量在闭包外部被修改,则闭包内部的值也会改变。这可能会导致程序的错误或意外行为。

Hello Word 2017

Python闭包中的陷阱

Python闭包中的陷阱 **陷阱1** def outer(): local=10 def inner(): local+=10 return local return inner resu=outer() """变量local是介于局部变量与全局变量之间的一种变量, 内层函数对其修改会报错,需要使用 nonlocal进行声明 """ def outer(): local=10 def inner(): n

冰点契约的博客 286

Python的诡异陷阱

编程的人,特别是学过c语言,使用过很长时间c的人,都会觉得,python这种语言跟matlab一样,没什么内涵,很easy。一开始也是这么想的,那是慢慢的,越来越觉得,人生苦短,我用python的理念其实不对。python完成一些小制作是很easy的事情,但是真正要成为一种工具,其实还是要考虑很多事情。近期实习的过程中,这种感觉越来越强烈了。         python看起来简单,其实内涵很复

lyx的专栏 2943

Python 中的常见陷阱

每一种语言都有各自的场景, 各自的设计, 有各自的坑. 近几年来使用场景越来越广泛的 Python, 都有哪些常见的坑(陷阱). 下面是我从初学以来, 碰到过的陷阱(跟其他语言不一样的地方). 1.函数内的变量作用域 函数内部, 代码块之内定义的变量,代码块之外可以访问。 i = 10 if i>5: square = i*i print(square) 在 Java/C#/C++/C 之类的语言中, 类似上面的代码逻辑会跑错, 因为 代码运行到最后一行的时候, s...

千里之行 341

Python-陷阱

所谓陷阱,也就是隐藏在表面之下,乍一看没问题,走过去会掉坑的存在。本文以 Python3 为准,介绍了部分容易掉坑的 Python 特性。

ManWZD的博客 186

Python 循环中的陷阱

Python 中的 for 循环和其他语言中的 for 循环工作方式是不一样的,今天就带你深入了解 Python 的 for 循环,看看它是如何工作的,以及它为什么按照这种方式工作。 循环中的陷阱 我们先来看一下 Python 循环中的「陷阱」,在我们了解了循环的工作方式后,再来看下这些陷阱到底是怎么出现的。 陷阱 1:循环两次 现在我们先假设有一个数字组成的列表,和一个用于返回这些数字的平方的生成器: >>> nums = [1, 2, 3, 4] >>> square

Python热爱者的博客 438

python默认参数陷阱_python 默认参数问题的陷阱

python 里面一个常见的陷阱就是函数的默认参数问题。如下:def func(mylist = []):mylist.append(1)return mylist以下的执行结果如下:print func()print func()print func()print func(['a'])print func()结果如下:[1][1, 1][1, 1, 1]['a', 1][1, 1, 1, 1]...

weixin_39523835的博客 293

Python常见陷阱

# -*- coding: utf-8 -*- #python需要常见陷阱 print u'---------------#1. 默认值参数 Default Parameter Values---------------' #1. 默认值参数 Default Parameter Values #1.1 默认值参数的引入 #默认值参数的作用:还可以使用默认参数、可变参数和关键字...

weixin_30897233的博客 173
上一篇: LeetCode 1 Two Sum
下一篇: LeetCode 2 Add Two Numbers
aying55555
博客等级 码龄12年 0粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值