Python笔记1-变量与对象
关注亿维讯达
一、Python 中,一切皆对象。每个对象由:标识(identity)、类型(type)、value(值)组成。
1. 标识用于唯一标识对象。使用内置函数 id(obj)可返回对象 obj 的标识。
2. 类型用于表示对象存储的“数据”的类型?梢允褂 type(obj)获得对象的所属类型。
3. 值表示对象所存储的数据的信息。使用 print(obj)可以直接打印出值。
4. Python 是动态类型语言,变量不需要显式声明类型。根据变量引用的对象,Python 解释器自动确定数据类型。
5. Python 是强类型语言,每个对象都有数据类型,只支持该类型支持的操作。
二、赋值
在 Python 中,变量也称为对象的引用。变量引用“对象”。
变量位于栈内存。对象位于堆内存。
变量盒子
Python 赋值语句实际上与“变量盒子”模型略有不同。在 Python 中,值可能最终放在内存中的任何位置,而变量用于引用它们。对变量赋值就像把一个黄色小粘贴便签放在值上,并说“这是 x”。
赋值就象贴标签
上图是一个更准确的 Python 赋值的效果。箭头用于显示变量引用的值。旧值不会被新值擦除,变量只需重新绑定新值。效果就像将粘贴便签从一个对象移动到另一个对象一样(重新绑定)。
你也不必担心计算机内存中充满“被丢弃”的值。如果一个值不再被任何变量引用,它就不再有用。 Python将自动从内存中清除这些值,以便空间可以用于存放新值。这个自动内存管理的过程确实被称为“垃圾收集”。
你也不必担心计算机内存中充满“被丢弃”的值。如果一个值不再被任何变量引用,它就不再有用。 Python将自动从内存中清除这些值,以便空间可以用于存放新值。这个自动内存管理的过程确实被称为“垃圾收集”。
变量在使用前必须先进行初始化,也就是将变量绑定在一个对象上,格式如:变量名 = 表达式。执行过程中,解释器先运行右边的表达式,在堆内存中创建一个对象,然后将对象的内存地址赋给左边的变量。
<section style="margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, " helvetica="" neue",="" "pingfang="" sc",="" "hiragino="" sans="" gb",="" "microsoft="" yahei="" ui",="" yahei",="" arial,="" sans-serif;="" font-size:="" 17px;="" text-align:="" justify;="" text-indent:="" 2em;"=""> 为了简化内存管理,Python通过引用计数机制实现自动垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。
不要将is用于数和字符串等不可变的基本值。鉴于Python在内部处理这些对象时可能会采用“small integer cache”的方式,将一些小整数、字符串等不可变对象只保留一份拷贝,这样做的结果是不可预测的。
三、 可变对象与不可变对象
Python在heap中分配的对象分成2类:
1. 不可变对象(immutable object):Number(int、float、bool、complex)、String、Tuple. 采用等效于“传引用”的方式。
2. 可变对象(mutable object):List、dictionary.采用等效于“传值”的方式。
四、关于参数传递
在编程语言设计中,有两种常见的范例将参数传递给函数:
传递值:参数的副本传递给函数。
传递引用:对参数的引用将传递给函数。
Python中的参数是按值传递还是按引用传递?答案是,两者都不是。
回想一下,在Python中,每条数据都是一个对象。引用指向一个对象,而不是一个特定的内存位置。
在Python中,类似的赋值语句如下:
x = 5
x = 10
这些赋值语句具有以下含义:
第一条语句导致x指向一个值为的对象5。
下x一条语句重新分配为值为的另一个对象的新引用10;痪浠八,第二个赋值重新绑定x到具有value的另一个对象10。
在Python中,当您将参数传递给函数时,会发生类似的重新绑定?悸且韵率纠
def f(fx):
fx = 10
x = 5
f(x)
---------------------
>>>x
>>>5
在主程序中,第x = 5的语句创建一个名为x的引用,该引用x绑定到一个值为的对象5。然后以x作为参数调用f()。当f()第一次启动时,一个叫做fx新的引用被创建,它最初指向对象x(也就是5):
函数调用图
但是,在执行fx = 10的语句时,将fx重新绑定 到值为10的新对象。x和fx这两个引用相互分离。没有影响主程序中的x,并且在f()终止x时仍会指向该对象5,就像在函数调用之前一样:
Python函数调用
可以使用id()确认。显示了所涉及对象的数字标识符:
>>> def f(fx):
2 ... print('fx =', fx, '/ id(fx) = ', id(fx))
3 ... fx = 10
4 ... print('fx =', fx, '/ id(fx) = ', id(fx))
5 ...
6
7 >>> x = 5
8 >>> print('x =', x, '/ id(x) = ', id(x))
9 x = 5 / id(x) = 1357924048
10
11 >>> f(x)
12 fx = 5 / id(fx) = 1357924048
13 fx = 10 / id(fx) = 1357924128
14
15 >>> print('x =', x, '/ id(x) = ', id(x))
16 x = 5 / id(x) = 1357924048
当f()第一次启动,fx与x都指向同一个对象,它id()是1357924048。f()执行语句fx = 10后,fx指向不同的对象,它id()是1357924128。调用环境中与原始对象的连接丢失。Python中的参数传递在某种程度上是按值传递和按引用传递之间的混合。传递给函数的是对对象的引用,但该引用是按值传递的。
注意: Python的参数传递机制称为pass-by-assignment。这是因为参数名称绑定到Python中函数输入上的对象,并且赋值也是将名称绑定到对象的过程。您可能还会看到术语“按对象传递”,“按对象传递引用”或“按共享传递”。
这里的关键要点是,Python函数无法通过将相应的参数重新分配给其他东西来更改参数的值。下面的示例演示了这一点:
>>> def f(x):
... x = 'foo'
...
>>> for i in (40,dict(foo=1, bar=2),{1, 2, 3},'bar',['foo', 'bar', 'baz']):
... f(i)
... print(i)
...
40
{'foo': 1, 'bar': 2}
{1, 2, 3}
bar
['foo', 'bar', 'baz']
在这里,类型的对象int,dict,set,str,,list传递给f()作为参数。但是正如您所看到的,一旦回到调用环境中,它们都保持不变。一旦f()执行任务x = 'foo',将被重新绑定,原始对象的连接丢失。
这是否意味着Python函数根本无法修改其参数?其实不,不是这样!注意这里发生的情况:
>>> def f(x):
... x[0] = '---'
...
>>> my_list = ['foo', 'bar', 'baz', 'qux']
>>> f(my_list)
>>> my_list
['---', 'bar', 'baz', 'qux']
在这种情况下,f()参数是list。当f()被调用时,一个引用my_list被传递。您已经看到f()无法重新分配my_list。但是,f()可以在内部使用引用进行修改my_list。在这里,f()已经修改了第一个元素。您可以看到,函数一旦返回,my_list实际上在调用环境中已被更改。相同的概念适用于字典:
>>> def f(x):
... x['bar'] = 22
...
>>> my_dict = {'foo': 1, 'bar': 2, 'baz': 3}
>>> f(my_dict)
>>> my_dict
{'foo': 1, 'bar': 22, 'baz': 3}
在这里,f()修改了my_dict。该更改反映在f()返回后的调用环境中。
参数传递摘要
Python中传递的参数可以总结如下。传递一个不可变的对象,像int,str,tuple,或frozenset,传递给Python函数的行为类似于按值传递。该函数无法在调用环境中修改对象。传递一个可变的对象,比如list、dict或set,可以起到类似于引用传递的作用,但不完全是这样。该函数不能完全重新分配对象,但它可以在对象中适当地更改项,这些更改将反映在调用环境中。
关注亿维讯达