开发中常遇到的Python陷阱和注意点-2

简介: 开发中常遇到的Python陷阱和注意点-2

问题一

但是,当我们通过下面的方式使用变量的时候,就会产生问题了:

num = 0
def var_func():
    print "num is :", num
    num = 1
var_func()

结果如下:

UnboundLocalError: local variable 'num' referenced before assignment

之所以产生这个错误,就是因为我们在var_func中给num变量进行了赋值,所以Python解释器会认为num是var_func作用域的本地变量,但是当代码执行到print "num is :", num语句的时候,num还是未定义。


问题二


上面的错误还是比较明显的,还有一种比较隐蔽的错误形式如下:

li = [1, 2, 3]
def foo():
    li.append(4)
    print li
foo()
def bar():
    li +=[5]
    print li
bar()

代码的结果为:

[1, 2, 3, 4]
UnboundLocalError: local variable 'li' referenced before assignment

在foo函数中,根据Python的作用域解析顺序,该函数中使用了全局的li变量;但是在bar函数中,对li变量进行了赋值,所以li会被当作bar作用域中的变量。

对于bar函数的这个问题,可以通过global关键字。

li = [1, 2, 3]
def foo():
    li.append(4)
    print li
foo()
def bar():
    global li
    li +=[5]
    print li
bar()

类属性隐藏

在Python中,有类属性和实例属性。类属性是属于类本身的,被所有的类实例共享。

类属性可以通过类名访问和修改,也可以通过类实例进行访问和修改。但是,当实例定义了跟类同名的属性后,类属性就被隐藏了。

看下面这个例子:

class Student(object):
    books = ["Python", "JavaScript", "CSS"]
    def __init__(self, name, age):
        self.name = name
        self.age = age
    pass
wilber = Student("Wilber", 27)
print "%s is %d years old" %(wilber.name, wilber.age)
print Student.books
print wilber.books
wilber.books = ["HTML", "AngularJS"]
print Student.books
print wilber.books
del wilber.books
print Student.books
print wilber.books

代码的结果如下,起初wilber实例可以直接访问类的books属性,但是当实例wilber定义了名称为books的实例属性之后,wilber实例的books属性就“隐藏”了类的books属性;当删除了wilber实例的books属性之后,wilber.books就又对应类的books属性了。

Wilber is 27 years old
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']
['HTML', 'AngularJS']
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']

当在Python值使用继承的时候,也要注意类属性的隐藏。对于一个类,可以通过类的__dict__属性来查看所有的类属性。


当通过类名来访问一个类属性的时候,会首先查找类的__dict__属性,如果没有找到类属性,就会继续查找父类。但是,如果子类定义了跟父类同名的类属性后,子类的类属性就会隐藏父类的类属性。


看一个例子:

class A(object):
    count = 1
class B(A):
    pass    
class C(A):
    pass        
print A.count, B.count, C.count      
B.count = 2
print A.count, B.count, C.count      
A.count = 3
print A.count, B.count, C.count     
print B.__dict__
print C.__dict__

结果如下,当类B定义了count这个类属性之后,就会隐藏父类的count属性

1 1 1
1 2 1
3 2 3
{'count': 2, '__module__': '__main__', '__doc__': None}
{'__module__': '__main__', '__doc__': None}

tuple是“可变的”

在Python中,tuple是不可变对象,但是这里的不可变指的是tuple这个容器总的元素不可变(确切的说是元素的id),但是元素的值是可以改变的。

tpl = (1, 2, 3, [4, 5, 6])
print id(tpl)
print id(tpl[3])
tpl[3].extend([7, 8])
print tpl
print id(tpl)
print id(tpl[3])

代码结果如下,对于tpl对象,它的每个元素都是不可变的,但是tpl[3]是一个list对象。也就是说,对于这个tpl对象,id(tpl[3])是不可变的,但是tpl[3]确是可变的。

36764576
38639896
(1, 2, 3, [4, 5, 6, 7, 8])
36764576
38639896

Python的深浅拷贝


在对Python对象进行赋值的操作中,一定要注意对象的深浅拷贝,一不小心就可能踩坑了。


当使用下面的操作的时候,会产生浅拷贝的效果:


使用切片[:]操作


使用工厂函数(如list/dir/set)


使用copy模块中的copy()函数


使用copy模块里面的浅拷贝函数copy():

import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.copy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

使用copy模块里面的深拷贝函数deepcopy():

import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.deepcopy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

模块循环依赖

在Python中使用import导入模块的时候,有的时候会产生模块循环依赖,例如下面的例子,module_x模块和module_y模块相互依赖,运行module_y.py的时候就会产生错误。

# module_x.py
import module_y
def inc_count():
    module_y.count += 1
    print module_y.count
# module_y.py
import module_x
count = 10
def run():
    module_x.inc_count()
run()

其实,在编码的过程中就应当避免循环依赖的情况,或者代码重构的过程中消除循环依赖。


当然,上面的问题也是可以解决的,常用的解决办法就是把引用关系搞清楚,让某个模块在真正需要的时候再导入(一般放到函数里面)。


对于上面的例子,就可以把module_x.py修改为如下形式,在函数内部导入module_y:

# module_x.py
def inc_count():
    import module_y
    module_y.count += 1
相关文章
|
1天前
|
前端开发 搜索推荐 编译器
【01】python开发之实例开发讲解-如何获取影视网站中经过保护后的视频-用python如何下载无法下载的视频资源含m3u8-python插件之dlp-举例几种-详解优雅草央千澈
【01】python开发之实例开发讲解-如何获取影视网站中经过保护后的视频-用python如何下载无法下载的视频资源含m3u8-python插件之dlp-举例几种-详解优雅草央千澈
【01】python开发之实例开发讲解-如何获取影视网站中经过保护后的视频-用python如何下载无法下载的视频资源含m3u8-python插件之dlp-举例几种-详解优雅草央千澈
|
13天前
|
IDE 测试技术 开发工具
10个必备Python调试技巧:从pdb到单元测试的开发效率提升指南
在Python开发中,调试是提升效率的关键技能。本文总结了10个实用的调试方法,涵盖内置调试器pdb、breakpoint()函数、断言机制、logging模块、列表推导式优化、IPython调试、警告机制、IDE调试工具、inspect模块和单元测试框架的应用。通过这些技巧,开发者可以更高效地定位和解决问题,提高代码质量。
106 8
10个必备Python调试技巧:从pdb到单元测试的开发效率提升指南
|
1天前
|
人工智能 编译器 Python
python已经安装有其他用途如何用hbuilerx配置环境-附带实例demo-python开发入门之hbuilderx编译器如何配置python环境—hbuilderx配置python环境优雅草央千澈
python已经安装有其他用途如何用hbuilerx配置环境-附带实例demo-python开发入门之hbuilderx编译器如何配置python环境—hbuilderx配置python环境优雅草央千澈
python已经安装有其他用途如何用hbuilerx配置环境-附带实例demo-python开发入门之hbuilderx编译器如何配置python环境—hbuilderx配置python环境优雅草央千澈
|
26天前
|
存储 API 数据库
使用Python开发获取商品销量详情API接口
本文介绍了使用Python开发获取商品销量详情的API接口方法,涵盖API接口概述、技术选型(Flask与FastAPI)、环境准备、API接口创建及调用淘宝开放平台API等内容。通过示例代码,详细说明了如何构建和调用API,以及开发过程中需要注意的事项,如数据库连接、API权限、错误处理、安全性和性能优化等。
87 5
|
2月前
|
机器学习/深度学习 人工智能 关系型数据库
Python开发
Python开发
42 7
|
2月前
|
前端开发 安全 数据库
使用Python开发独立站的全面指南
本文详细介绍了如何使用Python及其Web框架Django和Flask快速搭建功能完善、易于管理的独立站。从Python和Web开发基础讲起,逐步覆盖环境搭建、项目创建、数据库设计、视图与URL路由、模板创建、表单处理、测试调试、部署优化及安全维护等内容,旨在帮助开发者高效构建稳定的Web应用。
75 1
|
2月前
|
JSON API 数据格式
如何使用Python开发天猫获得淘宝买家秀API接口?
本文介绍了如何使用Python开发天猫和淘宝买家秀API接口,包括注册开放平台账号、创建应用获取API权限、构建请求URL、发送请求获取响应及解析数据等步骤,帮助开发者高效获取和处理商品信息与用户评价数据。
40 0
|
IDE 开发工具 Android开发
Python开发神器PyCharm,体验下吧
Python开发神器PyCharm,体验下吧
191 0
Python开发神器PyCharm,体验下吧
|
存储 监控 IDE
猪行天下之Python基础——1.3 Python开发IDE之PyCharm(下)
内容简述: 1、为什么要使用IDE? 2、PyCharm的下载安装 3、PyCharm的基本使用 4、PyCharm程序调试 5、共用全局的Python解释器
277 0
|
IDE 开发工具 开发者
猪行天下之Python基础——1.3 Python开发IDE之PyCharm(中)
内容简述: 1、为什么要使用IDE? 2、PyCharm的下载安装 3、PyCharm的基本使用 4、PyCharm程序调试 5、共用全局的Python解释器
192 0