按名调用 Algol

按值调用 Java

https://docs.python.org/3.6/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

https://docs.python.org/3.8/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

# 0-
# By returning a tuple of the results
def func2(a, b):
# a, b = 'new-value', b + 1
a = 'new-value'
b = b + 1 return a, b x, y = 'old-value', 99
x, y = func2(x, y) # 1-
# By using global variables. This isn’t thread-safe, and is not recommended. # 2-
# By passing a mutable (changeable in-place) object def func1(a):
a[0], a[1] = 'new-value', a[1] + 1 args = ['old-value', 99]
func1(args) # 3-
# By passing in a dictionary that gets mutated def func3(args):
args['a'], args['b'] = 'new-value', args['b'] + 1 args_b = {'a': 'old-value', 'b': 99}
func3(args_b) # 4-
# Or bundle up values in a class instance
class callByRef:
def __init__(self, **args):
for (key, value) in args.items():
setattr(self, key, value) def func4(args):
args.a, args.b = 'new-value', args.b + 1 args_c = callByRef(a='old-value', b=99)
func4(args_c)

Why did changing list ‘y’ also change list ‘x’?

If you wrote code like:

>>>

>>> x = []
>>> y = x
>>> y.append(10)
>>> y
[10]
>>> x
[10]

you might be wondering why appending an element to y changed x too.

There are two factors that produce this result:

  1. Variables are simply names that refer to objects. Doing y = x doesn’t create a copy of the list – it creates a new variable y that refers to the same object x refers to. This means that there is only one object (the list), and both x and y refer to it.

  2. Lists are mutable, which means that you can change their content.

After the call to append(), the content of the mutable object has changed from [] to [10]. Since both the variables refer to the same object, using either name accesses the modified value [10].

If we instead assign an immutable object to x:

>>>

>>> x = 5  # ints are immutable
>>> y = x
>>> x = x + 1 # 5 can't be mutated, we are creating a new object here
>>> x
6
>>> y
5

we can see that in this case x and y are not equal anymore. This is because integers are immutable, and when we do x = x + 1 we are not mutating the int 5 by incrementing its value; instead, we are creating a new object (the int 6) and assigning it to x (that is, changing which object x refers to). After this assignment we have two objects (the ints 6 and 5) and two variables that refer to them (x now refers to 6 but y still refers to 5).

Some operations (for example y.append(10) and y.sort()) mutate the object, whereas superficially similar operations (for example y = y + [10] and sorted(y)) create a new object. In general in Python (and in all cases in the standard library) a method that mutates an object will return None to help avoid getting the two types of operations confused. So if you mistakenly write y.sort() thinking it will give you a sorted copy of y, you’ll instead end up with None, which will likely cause your program to generate an easily diagnosed error.

However, there is one class of operations where the same operation sometimes has different behaviors with different types: the augmented assignment operators. For example, += mutates lists but not tuples or ints (a_list += [1, 2, 3] is equivalent to a_list.extend([1, 2, 3]) and mutates a_list, whereas some_tuple += (1, 2, 3) and some_int += 1 create new objects).

In other words:

  • If we have a mutable object (listdictset, etc.), we can use some specific operations to mutate it and all the variables that refer to it will see the change.

  • If we have an immutable object (strinttuple, etc.), all the variables that refer to it will always see the same value, but operations that transform that value into a new value always return a new object.

If you want to know if two variables refer to the same object or not, you can use the is operator, or the built-in function id().

How do I write a function with output parameters (call by reference)?

Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se. You can achieve the desired effect in a number of ways.

  1. By returning a tuple of the results:

    >>>

    >>> def func1(a, b):
    ... a = 'new-value' # a and b are local names
    ... b = b + 1 # assigned to new objects
    ... return a, b # return new values
    ...
    >>> x, y = 'old-value', 99
    >>> func1(x, y)
    ('new-value', 100)

    This is almost always the clearest solution.

  2. By using global variables. This isn’t thread-safe, and is not recommended.

  3. By passing a mutable (changeable in-place) object:

    >>>

    >>> def func2(a):
    ... a[0] = 'new-value' # 'a' references a mutable list
    ... a[1] = a[1] + 1 # changes a shared object
    ...
    >>> args = ['old-value', 99]
    >>> func2(args)
    >>> args
    ['new-value', 100]
  4. By passing in a dictionary that gets mutated:

    >>>

    >>> def func3(args):
    ... args['a'] = 'new-value' # args is a mutable dictionary
    ... args['b'] = args['b'] + 1 # change it in-place
    ...
    >>> args = {'a': 'old-value', 'b': 99}
    >>> func3(args)
    >>> args
    {'a': 'new-value', 'b': 100}
  5. Or bundle up values in a class instance:

    >>>

    >>> class Namespace:
    ... def __init__(self, /, **args):
    ... for key, value in args.items():
    ... setattr(self, key, value)
    ...
    >>> def func4(args):
    ... args.a = 'new-value' # args is a mutable Namespace
    ... args.b = args.b + 1 # change object in-place
    ...
    >>> args = Namespace(a='old-value', b=99)
    >>> func4(args)
    >>> vars(args)
    {'a': 'new-value', 'b': 100}

    There’s almost never a good reason to get this complicated.

Your best choice is to return a tuple containing the multiple results.

为什么更改列表 'y' 也会更改列表 'x'?

如果你编写的代码就像下面一样:

>>>

>>> x = []
>>> y = x
>>> y.append(10)
>>> y
[10]
>>> x
[10]

你可能想知道为什么追加一个元素也改变了x。

产生这种结果有两个因素:

  1. 变量只是指向具体对象的名称。 执行 y = x 并不会为列表创建一个副本 —— 它只是创建了一个新变量 y 指向 x 所指向的同一对象。 这意味着只存在一个对象(列表),x 和 y 都是对它的引用。

  2. 列表属于 mutable 对象,这意味着你可以改变它的内容。

在调用 append() 之后,这个可变对象的内容由 [] 变为 [10]。 由于两个变量都指向同一对象,因此使用任何一个名称所访问到的都是修改后的值 [10]

如果我们改为将不可变对象赋值给 x:

>>>

>>> x = 5  # ints are immutable
>>> y = x
>>> x = x + 1 # 5 can't be mutated, we are creating a new object here
>>> x
6
>>> y
5

我们可以看到在此情况下 x 和 y 就不再相等了。 这是因为整数是 immutable 对象,当我们执行 x = x + 1 时我们并不是改变了 5 这个对象的值;而是创建了一个新的对象 (整数 6) 并将其赋值给 x (也就是改变了 x 所指向的对象)。 在赋值之后我们就有了两个对象 (整数 6 和 5) 以及分别指向它们的两个变量 (x 现在指向 6 而 y 仍然指向 5)。

某些操作 (例如 y.append(10) 和 y.sort()) 是改变原对象,而看上去相似的另一些操作 (例如 y = y + [10] 和 sorted(y)) 则是创建新对象。 通常在 Python 中 (以及在标准库的所有代码中) 会改变原对象的方法将返回 None 以帮助避免混淆这两种不同类型的操作。 因此如果你错误地使用了 y.sort() 并期望它将返回一个经过排序的 y 的副本,你得到的结果将会是 None,这将导致你的程序产生一个容易诊断的错误。

但是,还存在一类操作,不同的类型执行相同的操作会有不同的行为:那就是增强赋值运算符。 例如,+= 会原地改变列表,但不会改变元组或整数 (a_list += [1, 2, 3] 与 a_list.extend([1, 2, 3]) 一样都会改变 a_list,而 some_tuple += (1, 2, 3) 和 some_int += 1 则会创建新的对象)。

换而言之:

  • 如果我们有一个可变对象 (listdictset 等等),我们可以使用某些特定的操作来改变它,所有指向它的变量都会显示它的改变。

  • 如果我们有一个不可变对象 (strinttuple 等等),所有指向它的变量都将显示相同样的值,但凡是会改变这个值的操作将总是返回一个新对象。

如果你想知道两个变量是否指向相同的对象,你可以使用 is 运算符,或内置函数 id()

如何编写带输出参数的函数(通过引用调用)?

请记住在 Python 中参数是通过赋值来传递的。 由于赋值只是创建了对象的引用,因此在调用者和被调用者的参数名称之间没有别名,所以本身是没有按引用调用的。 你可以通过多种方式实现所需的效果。

  1. 通过返回一个结果元组:

    >>>

    >>> def func1(a, b):
    ... a = 'new-value' # a and b are local names
    ... b = b + 1 # assigned to new objects
    ... return a, b # return new values
    ...
    >>> x, y = 'old-value', 99
    >>> func1(x, y)
    ('new-value', 100)

    这几乎总是最清晰明了的解决方案。

  2. 通过使用全局变量。 这种方式不是线程安全的,而且也不受推荐。

  3. 通过传递一个可变 (即可原地修改的) 对象:

    >>>

    >>> def func2(a):
    ... a[0] = 'new-value' # 'a' references a mutable list
    ... a[1] = a[1] + 1 # changes a shared object
    ...
    >>> args = ['old-value', 99]
    >>> func2(args)
    >>> args
    ['new-value', 100]
  4. 通过传递一个会被改变的字典:

    >>>

    >>> def func3(args):
    ... args['a'] = 'new-value' # args is a mutable dictionary
    ... args['b'] = args['b'] + 1 # change it in-place
    ...
    >>> args = {'a': 'old-value', 'b': 99}
    >>> func3(args)
    >>> args
    {'a': 'new-value', 'b': 100}
  5. 或者在一个类实例中捆绑值:

    >>>

    >>> class Namespace:
    ... def __init__(self, /, **args):
    ... for key, value in args.items():
    ... setattr(self, key, value)
    ...
    >>> def func4(args):
    ... args.a = 'new-value' # args is a mutable Namespace
    ... args.b = args.b + 1 # change object in-place
    ...
    >>> args = Namespace(a='old-value', b=99)
    >>> func4(args)
    >>> vars(args)
    {'a': 'new-value', 'b': 100}

    几乎没有任何适当理由将问题如此复杂化。

你的最佳选择是返回一个包含多个结果的元组。

Is Python call-by-value or call-by-reference? Neither. https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/

Is Python call-by-value or call-by-reference? Neither.

One aspect of Python programming that trips up those coming from languages like C or Java is how arguments are passed to functions in Python. At a more fundamental level, the confusion arises from a misunderstanding about Python object-centric data model and its treatment of assignment. When asked whether Python function calling model is "call-by-value" or "call-by-reference", the correct answer is: neither. Indeed, to try to shoe-horn those terms into a conversation about Python's model is misguided. "call-by-object," or "call-by-object-reference" is a more accurate way of describing it. But what does "call-by-object" even mean?

In Python, (almost) everything is an object. What we commonly refer to as "variables" in Python are more properly called names. Likewise, "assignment" is really the binding of a name to an object. Each binding has a scope that defines its visibility, usually the block in which the name originates.

That's a lot of terminology all at once, but those basic terms form the cornerstone of Python's execution model. Compared to, say, C++, the differences are subtle yet important. A concrete example will highlight these differences. Think about what happens when the following C++ code is executed:

1
2
3
string some_guy = "Fred";
// ...
some_guy = "George";

In the above, the variable some_guy refers to a location in memory, and the value 'Fred' is inserted in that location (indeed, we can take the address of some_guy to determine the portion of memory to which it refers). Later, the contents of the memory location referred to by some_guyare changed to 'George'. The previous value no longer exists; it was overwritten. This likely matches your intuitive understanding (even if you don't program in C++).

Let's now look at a similar block of Python code:

1
2
3
some_guy = 'Fred'
# ...
some_guy = 'George'

Binding Names to Objects

On line 1, we create a binding between a namesome_guy, and a string object containing 'Fred'. In the context of program execution, the environment is altered; a binding of the name some_guy' to a string object is created in the scope of the block where the statement occurred. When we later say some_guy = 'George', the string object containing 'Fred' is unaffected. We've just changed the binding of the name some_guy. We haven't, however, changed either the 'Fred' or 'George' string objects. As far as we're concerned, they may live on indefinitely.

With only a single name binding, this may seem overly pedantic, but it becomes more important when bindings are shared and function calls are involved. Let's say we have the following bit of Python code:

 1
2
3
4
5
6
7
8
9
10
some_guy = 'Fred'

first_names = []
first_names.append(some_guy) another_list_of_names = first_names
another_list_of_names.append('George')
some_guy = 'Bill' print (some_guy, first_names, another_list_of_names)

So what get's printed in the final line? Well, to start, the binding of some_guy to the string object containing 'Fred' is added to the block's namespace. The name first_names is bound to an empty list object. On line 4, a method is called on the list object first_names is bound to, appending the object some_guy is bound to. At this point, there are still only two objects that exist: the string object and the list object. some_guyand first_names[0] both refer to the same object (Indeed, print(some_guy is first_names[0]) shows this).

Let's continue to break things down. On line 6, a new name is bound: another_list_of_names. Assignment between names does not create a new object. Rather, both names are simply bound to the same object. As a result, the string object and list object are still the only objects that have been created by the interpreter. On line 7, a member function is called on the object another_list_of_names is bound to and it is mutated to contain a reference to a new object: 'George'. So to answer our original question, the output of the code is

Bill ['Fred', 'George'] ['Fred', 'George']

This brings us to an important point: there are actually two kinds of objects in Python. A mutable object exhibits time-varying behavior. Changes to a mutable object are visible through all names bound to it. Python's lists are an example of mutable objects. An immutable object does not exhibit time-varying behavior. The value of immutable objects can not be modified after they are created. They can be used to compute the values of new objects, which is how a function like string.join works. When you think about it, this dichotomy is necessary because, again, everything is an object in Python. If integers were not immutable I could change the meaning of the number '2' throughout my program.

It would be incorrect to say that "mutable objects can change and immutable ones can't", however. Consider the following:

1
2
3
4
5
first_names = ['Fred', 'George', 'Bill']
last_names = ['Smith', 'Jones', 'Williams']
name_tuple = (first_names, last_names) first_names.append('Igor')

Tuples in Python are immutable. We can't change the tuple object name_tuple is bound to. But immutable containers may contain references to mutable objects like lists. Therefore, even though name_tuple is immutable, it "changes" when 'Igor' is appended to first_names on the last line. It's a subtlety that can sometimes (though very infrequently) prove useful.

By now, you should almost be able to intuit how function calls work in Python. If I call foo(bar), I'm merely creating a binding within the scope of foo to the object the argument bar is bound to when the function is called. If bar refers to a mutable object and foo changes its value, then these changes will be visible outside of the scope of the function.

1
2
3
4
5
6
7
8
9
def foo(bar):
bar.append(42)
print(bar)
# >> [42] answer_list = []
foo(answer_list)
print(answer_list)
# >> [42]

On the other hand, if bar refers to an immutable object, the most that foo can do is create a name bar in its local namespace and bind it to some other object.

1
2
3
4
5
6
7
8
9
def foo(bar):
bar = 'new value'
print (bar)
# >> 'new value' answer_list = 'old value'
foo(answer_list)
print(answer_list)
# >> 'old value'

Hopefully by now it is clear why Python is neither "call-by-reference" nor "call-by-value". In Python a variable is not an alias for a location in memory. Rather, it is simply a binding to a Python object. While the notion of "everything is an object" is undoubtedly a cause of confusion for those new to the language, it allows for powerful and flexible language constructs, which I'll discuss in my next post.

Posted on Nov 13, 2012 by Jeff Knupp

实践

Python 3.7.7
>>> a=[]
>>> b=a
>>> b.append(1)
>>> id(a)
140139695586672
>>> id(b)
140139695586672
>>> c=a
>>> c=[1]
>>> id(c)
140139696409648
>>> c=[1,2]
>>> id(c)
140139695049536
>>> d=a
>>> id(d)
140139695586672
>>> id(a)
140139695586672
>>> d=[1]
>>> id(d)
140139696409648
>>>

  

d = {'a': {'sa': 'vsa'}, 'b': {'sb': 'vsb'}}
d_all = {'sm': 'smv', 'sn': 'snv'}

d_merged = d
print(d_merged == d)
for i in d:
for j in d_all:
d[i][j] = d_all[j]

print(d_merged == d)
import sys

print(sys.version_info)

True
True
sys.version_info(major=3, minor=8, micro=2, releaselevel='final', serial=0)

call by value reference name python既不是按值传递也不是按引用传递 python复制原理 创建新对象 与 改变原对象的更多相关文章

  1. Python中的可变对象与不可变对象、浅拷贝与深拷贝

    Python中的对象分为可变与不可变,有必要了解一下,这会影响到python对象的赋值与拷贝.而拷贝也有深浅之别. 不可变对象 简单说就是某个对象存放在内存中,这块内存中的值是不能改变的,变量指向这块 ...

  2. Python可变对象和不可变对象

    Python中一切皆对象,每个对象都有其唯一的id,对应的类型和值,其中id指的是对象在内存中的位置.根据对象的值是否可修改分为可变对象和不可变对象.其中, 不可对象包括:数字,字符串,tuple 可 ...

  3. windows和linux中搭建python集成开发环境IDE——如何设置多个python环境

    本系列分为两篇: 1.[转]windows和linux中搭建python集成开发环境IDE 2.[转]linux和windows下安装python集成开发环境及其python包 3.windows和l ...

  4. 精通 Oracle+Python,第 7 部分:面向服务的 Python 架构

    面向服务的架构 (SOA) 在当今的业务战略中具有至关重要的作用.混搭企业组件已成为所有任务关键的企业应用程序的标准要求,从而确保在企业架构的各层实现顺畅的服务编排.对此,Python 是一个不错的选 ...

  5. 可变对象 vs 不可变对象(Python)

    Python 在 heap 中分配的对象分成两类:可变对象和不可变对象.所谓可变对象是指,对象的内容是可变的,例如 list.而不可变的对象则相反,表示其内容不可变. 不可变对象:int,string ...

  6. 《python解释器源码剖析》第9章--python虚拟机框架

    9.0 序 下面我们就来剖析python运行字节码的原理,我们知道python虚拟机是python的核心,在源代码被编译成字节码序列之后,就将有python的虚拟机接手整个工作.python虚拟机会从 ...

  7. 《python解释器源码剖析》第8章--python的字节码与pyc文件

    8.0 序 我们日常会写各种各样的python脚本,在运行的时候只需要输入python xxx.py程序就执行了.那么问题就来了,一个py文件是如何被python变成一系列的机器指令并执行的呢? 8. ...

  8. py, pyc, pyw, pyo, pyd Compiled Python File (.pyc) 和Java或.NET相比,Python的Virtual Machine距离真实机器的距离更远

    https://my.oschina.net/renwofei423/blog/17404 1.      PyCodeObject与Pyc文件 通常认为,Python是一种解释性的语言,但是这种说法 ...

  9. 【Python五篇慢慢弹】快速上手学python

    快速上手学python 作者:白宁超 2016年10月4日19:59:39 摘要:python语言俨然不算新技术,七八年前甚至更早已有很多人研习,只是没有现在流行罢了.之所以当下如此盛行,我想肯定是多 ...

随机推荐

  1. android studio 找不到真机设备

    连接USB之后没有显示连接,如下图 设备管理器: 解决:重启电脑

  2. JVM笔记【1】-- 运行时数据区

    目录 (一)java内存区域管理 1.1 程序计数器 1.2 虚拟机栈 1.3 本地方法栈 1.4 java堆 1.5 方法区 1.5.1 运行时常量池 (二)直接内存 (一)java内存区域管理 C ...

  3. [leetcode]103. Binary Tree Zigzag Level Order Traversal二叉树Z字形层序遍历

    相对于102题,稍微改变下方法就行 迭代方法: 在102题的基础上,加上一个变量来判断是不是需要反转 反转的话,当前list在for循环结束后用collection的反转方法就可以实现反转 递归方法: ...

  4. Python & Matplotlib: Monte Carlos Method

    Hey! 这里是Lindy:) Hope you guys are doing well! 今天想记录的概念叫做 蒙特·卡罗 方法,是今年在cs课上老师做的扩展延伸.其实我在初次接触这个概念时觉得很新 ...

  5. 如何理解SQL的可重复读和幻读之间的区别?

    从本源来理解比较容易理解,如果只是描述概念和定义,容易让人云里雾里找不到方向.正好这两天在浏览mysql的文档,我可以简单在这里总结一下,帮助其他还没有理解的朋友,如果有错误也麻烦帮忙指正. 先讲一点 ...

  6. python常用操作和内置函数

    一.常用数据处理方法. 1.索引:按照号码将对应位置的数据取出使用 2.list将任意类型数据用逗号分割存在列表中 3.range:产生一堆数字(顾头不顾尾) 4.切片:可以从复制数据的一部分,不影响 ...

  7. Java中常用修饰符浅谈

    一.public.protected.default和private修饰符的作用域 public:在java程序中,如果将属性和方法定义为 public 类型,那么此属性和方法所在的类和及其子类,同一 ...

  8. Web Service 服务无法连接Oracle数据库

    这个问题之前部署就遇到过,但是后来忘了,所以记录一下吧. 我部署Web Service服务的时候,服务没法正常运行,与Oracle数据库无法正常通信. 检查了数据库连接字没有任何问题,写了个测试接口, ...

  9. 百度智能(文本识别),API传图OC代码与SDK使用

    百度智能中的文本识别中的身份证识别,有API方式和SDK方式 API方式 百度智能(文本识别),百度API传图没有提供OC的示例,这里提供一下 - (void)OCTest:(NSString*)to ...

  10. TypeScript接口与类的使用

    一.TypeScript接口 Interfaces 可以约定一个对象的结构 一个对象去实现一个接口 就必须拥有这个接口中所有的成员用interface定义接口, 并且定义接口中成员的类型 编译之后会发 ...