目录 | 上一节 (2.6 列表推导式) | [下一节 (3 程序组织)]()
2.7 对象
本节介绍无关 Python 外部对象模型的更多详细信息,并探讨一些与内存治理,拷贝和类型查看无关的问题。
赋值
Python 中的许多操作都与赋值或者存储值无关。
a = value # Assignment to a variables[n] = value # Assignment to a lists.append(value) # Appending to a listd['key'] = value # Adding to a dictionary
正告:赋值操作永远不是值拷贝。所有的赋值操作都是援用拷贝(如果你乐意,也能够说是指针拷贝)
赋值示例
思考该代码片段:
a = [1,2,3]b = ac = [a,b]
以下是底层内存操作图。在此示例中,只有一个列表对象 [1,2,3]
,然而有四个不同的援用指向它。
这意味着批改一个值会影响所有的援用。
>>> a.append(999)>>> a[1,2,3,999]>>> b[1,2,3,999]>>> c[[1,2,3,999], [1,2,3,999]]>>>
请留神,原始列表中的更改是如何在其它中央显示的。这是因为从未进行任何拷贝,所有的货色都指向同一个货色。
从新赋值
从新赋值永远不会重写之前的值所应用的内存。
a = [1,2,3]b = aa = [4,5,6]print(a) # [4, 5, 6]print(b) # [1, 2, 3] Holds the original value
切记:变量是名称,不是内存地址
危险
如果你不晓得这种(数据)共享(的形式),那么在某些时候你会搬起石头砸本人的脚。典型情景,你批改了一些数据,认为它是本人的公有拷贝,然而它却意外地损毁坏了程序其它局部的某些数据。
阐明:这就是为什么原始数据类型是不可变(只读)的起因之一
标识值和援用
应用 is
操作符查看两个值是否真的是雷同的对象。
>>> a = [1,2,3]>>> b = a>>> a is bTrue>>>
is
操作符比拟对象的标识值(一个整数)。标识值能够应用 id()
函数获取。
>>> id(a)3588944>>> id(b)3588944>>>
留神:应用 ==
查看对象是否相等简直总是更好,is
的后果通常会出其不意:
>>> a = [1,2,3]>>> b = a>>> c = [1,2,3]>>> a is bTrue>>> a is cFalse>>> a == cTrue>>>
浅拷贝
列表和字典本身具备用于拷贝的办法。
>>> a = [2,3,[100,101],4]>>> b = list(a) # Make a copy>>> a is bFalse
这是一个新列表,然而列表中的项是共享的。
>>> a[2].append(102)>>> b[2][100,101,102]>>>>>> a[2] is b[2]True>>>
例如,外部列表 [100, 101, 102]
正在共享。这就是众所皆知的浅拷贝。上面是图示:
深拷贝
有时候,须要拷贝一个对象及其中所蕴含的所有对象,为此,能够应用 copy
模块:
>>> a = [2,3,[100,101],4]>>> import copy>>> b = copy.deepcopy(a)>>> a[2].append(102)>>> b[2][100,101]>>> a[2] is b[2]False>>>
名称,值,类型
变量名称没有类型,仅仅是一个名字。然而,值的确具备一个底层的类型。
>>> a = 42>>> b = 'Hello World'>>> type(a)<type 'int'>>>> type(b)<type 'str'>
type()
函数将通知你这是什么。类型名称通常用作创立或将值转换为该类型的函数。
类型查看
如何判断对象是否为特定类型?
if isinstance(a, list): print('a is a list')
查看是否是多种类型中的一种:
if isinstance(a, (list,tuple)): print('a is a list or tuple')
留神:不要适度应用类型查看。这会导致适度的代码复杂性。通常,如果这样做可能阻止其他人在应用你的代码时犯常见谬误,那么就应用类型查看。
所有皆对象
数字,字符串,列表,函数,异样,类,实例等都是对象。这意味着所有能够命名的对象都能够作为数据传递、搁置到容器中,而没有任何限度。没有非凡的对象。有时,能够这样说,所有的对象都是“一等对象”。
一个简略的例子:
>>> import math>>> items = [abs, math, ValueError ]>>> items[<built-in function abs>, <module 'math' (builtin)>, <type 'exceptions.ValueError'>]>>> items[0](-45)45>>> items[1].sqrt(2)1.4142135623730951>>> try: x = int('not a number') except items[2]: print('Failed!')Failed!>>>
在这里,items
是一个蕴含函数,模块和异样的列表。能够间接应用列表中的项代替原始名称。
items[0](-45) # absitems[1].sqrt(2) # mathexcept items[2]: # ValueError
权力越大,责任越大。只是因为你能够做,但并象征这你应该这样做。
练习
在这组练习中,咱们来看看来自一等对象的威力。
练习 2.24:一等数据
在 Data/portfolio.csv
文件中,咱们把有组织的数据读取为列,如下所示:
name,shares,price"AA",100,32.20"IBM",50,91.10...
在之前的代码中,咱们应用 csv
模块读取文件,然而仍必须手动执行类型转换。例如:
for row in rows: name = row[0] shares = int(row[1]) price = float(row[2])
也能够应用一些列表基本操作以更奇妙的形式来执行这种转换。
创立一个蕴含转换函数名称的 Python 列表,这些函数用来把每一列转换成适当的类型。
>>> types = [str, int, float]>>>
能够创立这样的列表是因为在 Python 中所有皆一等对象。所以,如果想创立一个函数列表,也是能够的。列表中创立的项用于将值 x
转换为给定的类型(如:str(x)
, int(x)
, float(x)
)。
当初,从下面文件的数据中读取一行:
>>> import csv>>> f = open('Data/portfolio.csv')>>> rows = csv.reader(f)>>> headers = next(rows)>>> row = next(rows)>>> row['AA', '100', '32.20']>>>
如前所述,该行不足以进行计算,因为类型是谬误的。例如:
>>> row[1] * row[2]Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: can't multiply sequence by non-int of type 'str'>>>
然而,兴许数据能够与在 types
中指定的类型配对。例如:
>>> types[1]<type 'int'>>>> row[1]'100'>>>
尝试转换其中一个值:
>>> types[1](row[1]) # Same as int(row[1])100>>>
尝试转换另一个值:
>>> types[2](row[2]) # Same as float(row[2])32.2>>>
尝试应用转换后的值进行计算:
>>> types[1](row[1])*types[2](row[2])3220.0000000000005>>>
应用 zip() 函数将字段组合到一起,并且查看后果:
>>> r = list(zip(types, row))>>> r[(<type 'str'>, 'AA'), (<type 'int'>, '100'), (<type 'float'>,'32.20')]>>>
留神看,这会将类型转换函数名称与值配对。例如,int
和 '100'
配对。
如果要一个接一个地对所有值进行转换,那么合并后的列表很有用。请尝试:
>>> converted = []>>> for func, val in zip(types, row): converted.append(func(val))...>>> converted['AA', 100, 32.2]>>> converted[1] * converted[2]3220.0000000000005>>>
确保你了解上述代码中所产生的事件。在循环中,func
变量是类型转换函数(如str
, int
等 )之一且 val
变量是值('AA'
, '100'
)之一。表达式 func(val)
转换一个值(相似于类型转换)。
下面的代码能够转换为单个列表推导式。
>>> converted = [func(val) for func, val in zip(types, row)]>>> converted['AA', 100, 32.2]>>>
练习 2.25:创立字典
还记得如果有一个键和值的序列,如何应用dict()
函数轻松地创立字典吗?让咱们从列题目创立一个字典吧:
>>> headers['name', 'shares', 'price']>>> converted['AA', 100, 32.2]>>> dict(zip(headers, converted)){'price': 32.2, 'name': 'AA', 'shares': 100}>>>
当然,如果你精通列表推导式,则能够应用字典推导式一步实现整个转换。
>>> { name: func(val) for name, func, val in zip(headers, types, row) }{'price': 32.2, 'name': 'AA', 'shares': 100}>>>
练习 2.26:全局
应用本练习中的技术,能够编写语句,轻松地将简直任何面向列的数据文件中的字段转换为 Python 字典。
为了阐明,假如你像上面这样从不同的数据文件读取数据,如下所示:
>>> f = open('Data/dowstocks.csv')>>> rows = csv.reader(f)>>> headers = next(rows)>>> row = next(rows)>>> headers['name', 'price', 'date', 'time', 'change', 'open', 'high', 'low', 'volume']>>> row['AA', '39.48', '6/11/2007', '9:36am', '-0.18', '39.67', '39.69', '39.45', '181800']>>>
让咱们应用相似的技巧来转换字段:
>>> types = [str, float, str, str, float, float, float, float, int]>>> converted = [func(val) for func, val in zip(types, row)]>>> record = dict(zip(headers, converted))>>> record{'volume': 181800, 'name': 'AA', 'price': 39.48, 'high': 39.69,'low': 39.45, 'time': '9:36am', 'date': '6/11/2007', 'open': 39.67,'change': -0.18}>>> record['name']'AA'>>> record['price']39.48>>>
附加题:如何批改本示例以进一步解析 date
条目到元组中,如(6, 11, 2007)
?
请花一些工夫认真思考你在练习中所做的事件。咱们稍后会再次探讨这些想法。
目录 | 上一节 (2.6 列表推导式) | [下一节 (3 程序组织)]()
注:残缺翻译见 https://github.com/codists/practical-python-zh