【问题标题】:Is it Possible in python, to Pass Variables Within a List, by Reference, in an Argument? [duplicate]在 python 中,是否可以在参数中通过引用在列表中传递变量? [复制]
【发布时间】:2021-06-29 00:48:28
【问题描述】:

我正在尝试创建一个由连接在一起的神经元对象组成的神经网络类。

我的神经元课有

  1. 树突
    树突的数量在类初始化时在参数中指定。树突存储在一个列表中,其索引存储每个树突的电压。 例如:neuron1.dendrites[2]=0.12 伏特。
  2. 激活(阈值)潜力 所有树突电位的总和提供了神经元电位。如果这个电位超过阈值电位,我的神经元就会发射。还有其他神经元连接到我神经元的轴突。我的神经元的轴突连接到其他神经元对象的树突。来自其他神经元的几个树突可能会连接到我的神经元的轴突。当我的神经元触发时,它们都会收到一个固定的电压(输出电压)。它以全有或全无的方式触发。
  3. 发射状态
    当达到激活电位时,触发状态 = on (True)
  4. 我的 Neuron 类也有一个 setConnections() 方法。此方法接收树突的 python list。在这种方法中,我希望遍历外部 Dendrite 的内部列表并重置它们的电压值。 这不起作用。我不知道为什么,所以在这里寻求帮助。

我在下面提供了我的代码的精简版本:

import threading

 
 class Neuron:
      def __init__(self, dendrites, activation_Pot=0.24):
      """
      Create a dendrites[] array.
      Each element of this array represents the voltage of that dendrite.
      We can then loop through the array to sum up the signal strength.  

      If the signal strength exceeds the Activation potential, then the all-or-nothing threshold has been breached and
      we can transmit our signal along the axon.
      """
      self.dendrites = [0]*dendrites
      self.InputPotential = 0  # This variable will store the sum of all the dendrite voltages.  It is being initialised here.
      self.activation_Pot = activation_Pot
      self.on = True
      self.off = False
      self.voltsOut = 0.12      # This constant dictates the potential of the axon when the neuron is firing.
      self.outputPotential = 0  # This variable  SETS the potential of the single axon when the threshold activation potential of the  neuron has been reached and the neuron is firing.
      self.firing = self.off
      self.axonConnections = []
      
      # Launch a thread to check on a timer the sum of all dendrite inputs and fire when output > Activation Potential.
      t1 = threading.Thread(target = self.start, args=[])
      t1.start()
      
      
      def fire(self):
          self.outputPotential = self.voltsOut
          self.firing = self.on
          print("Neuron is firing!")
          
          for outputDendrites in self.axonConnections:
              outputDendrites = self.outputPotential
              
      def stopFiring(self):
          self.outputPotential = 0
          self.firing = self.off
          print("Neuron has STOPPED firing!")
          
          
      def setActivation_Pot(self, activation_Pot):
          if (activation_Pot >= 0) and (activation_Pot <=1):
              self.activation_Pot = activation_Pot
          else:
              print("activation_Pot value needs to be between 0 and 1")
              print("activation_Pot has not been reset.")
              print("Please consider re-inputting a valid value.")        
              
              
      def getActivation_Pot(self):
          return self.activation_Pot
          
          
      def setAxonConnections(self, axonConnections):
          self.axonConnections = axonConnections
          
      def getAxonConnections(self):
          return self.axonConnections
      
      
      def start(self):
          while True:
              while True:
                  self.InputPotential = 0  
                  for dendrite in self.dendrites:
                      self.InputPotential+=dendrite
                      
                  if self.InputPotential >= self.activation_Pot:
                      self.fire()
                      break
              while True:
                  self.InputPotential = 0
                  for dendrite in self.dendrites:
                      self.InputPotential+=dendrite
                  
                  if self.InputPotential < self.activation_Pot:
                      self.stopFiring()
                      break

以下是来自 main.py 脚本的相关代码,用于测试 Neuron 类:

from neuron import Neuron

# Instantiate transmitting neurone...
n0 = Neuron(3, 0.36)

# Instantiate receiving neurones...
n1 = Neuron(3, 0.36)
n2 = Neuron(3, 0.36)
n3 = Neuron(3, 0.36)

# Make the connections: I do this by creating storing my external Dendrites into a list 
# and passing that list to the transmitting neuron for it to update the voltages 
# of each of these neurons.  BUT THESE LIST VARIABLES ARE NOT GETTING UPDATED...
axonConns = [n1.dendrites[0], n2.dendrites[1], n3.dendrites[2]]
n0.setAxonConnections(axonConns) # THE LIST VARIABLES OF THE axonConns LIST ARE NOT GETTING UPDATED!!

n0.fire()  # THE LIST VARIABLES OF THE axonConns LIST ARE NOT GETTING UPDATED by this fire() method!!

我希望这是有道理的。总而言之:我在 n0.setAxonConnections(axonConns) 行传递了来自 main.py 脚本的变量列表。我的 Neuron 类的 neuron.fire() 方法没有更新此列表中的变量。有人可以解释为什么吗?原谅我,我是python新手!

【问题讨论】:

  • axonConns 不是“变量列表”。它是从一组变量派生的值列表。此外,您认为应该更新这些变量 (outputDendrites = self.outputPotential) 的行(我认为)只是分配给一个局部变量,该变量之前恰好具有您列表中的值。
  • @ScottHunter,我明白你在说什么。但我才刚刚开始学习python(字面意思是几天前)。有解决办法吗?在某些语言(例如 VB6.0)中,可以通过引用传递参数。这在 python 中可行吗?
  • 完成这项工作的一种快速方法是:axonConns = [(n1.dendrites, 0), (n2.dendrites, 1), (n3.dendrites, 3)] 并使用fire() 处理它时牢记索引。您需要传递列表本身,以按照您打算的方式反映更改。这是假设您希望更新 n1、n2 和 n3。但如果不是,那么刚刚从@aebange 传来的答案就足够了。
  • 不,python 不支持引用调用。

标签: python python-3.x list variables


【解决方案1】:

可能不是最好的解决方案,但只是我的 2c。如果您希望其他神经元的树突也被更新,您可以像这样声明连接:

axonConns = [(n1.dendrites, 0), (n2.dendrites, 1), (n3.dendrites, 2)]

您需要传递树突本身的列表,并通过索引定义要在连接中考虑哪些树突。然后更改fire 方法以考虑索引:

def fire(self):
    self.outputPotential = self.voltsOut
    self.firing = self.on
    print("Neuron is firing!")
    
    for dendrites, index in self.axonConnections:
        dendrites[index] = self.outputPotential

编辑:

为了证明为什么OP的答案不足以更新fire()之外的神经元

In [1]: x = [1, 2, 3]
   ...:
   ...: def foo(val):
   ...:     potential = 100
   ...:     for i in range(len(val)):
   ...:         val[i] = potential
   ...:     return val
   ...:
   ...: print(x)
   ...: print(foo([x[0], x[1], x[2]]))
   ...: print(x)
   ...:
[1, 2, 3]
[100, 100, 100]
[1, 2, 3]

【讨论】:

  • @ bdbd 回应您在我自己的解决方案下的评论,我现在已删除:奇怪! axonConns 列表已更新(让我相信它有效)但个别元素没有。换句话说,你是对的。树突本身没有更新。我将在当天晚些时候再看一下。
  • 是的,在这种方法中axonConns 将被更新,但仅在神经元放电的上下文中更新,即n0。但是在其他神经元的上下文中,什么都不会改变。
  • 谢谢,您的解决方案奏效了!
【解决方案2】:

通过在 python 中使用以下代码,我认为我找到了解决方案:

def changeListVars(n):
    for i in range(len(n)):
        n[i] = n[i]+5
    print()
    print(n)

x=1
y=2
z=3
m = [x,y,z]
print(len(m))

for i in range(len(m)):
    print (m[i])
    
changeListVars(m)
print()
print(m)

上面的输出是:

3
1
2
3

[6, 7, 8]

[6, 7, 8]

最初,这似乎有效。因此,为了修复我的代码,我修改了这两行:

        for i in range(len(self.axonConnections)):
            self.axonConnections[i] = self.outputPotential

所以我的新fire() 方法如下:

def fire(self):
    self.outputPotential = self.voltsOut
    self.firing = self.on
    print("Neuron is firing!")
    
    for i in range(len(self.axonConnections)):
        self.axonConnections[i] = self.outputPotential

上述实验的结果清楚地表明,python 确实在传递参数时默认传递列表的变量,通过引用

但是(感谢@bdbd)我发现列表的元素保持不变。比如,

print(x, y, z)

产量

1 2 3

这也意味着在我的代码中,虽然axonConn 数组(存储外部枝晶电压列表)正在更新,但枝晶本身并未更新。

经过一番研究和实验,我终于解决了这个难题。首先是necessary background reading:

所有 Python 对象都有:

  1. 唯一标识(一个整数,由 id(x) 返回)
  2. 一个类型(由 type(x) 返回)
  3. 一些内容

你不能改变身份!

你不能改变类型!

某些对象允许您更改其内容(无需更改 身份或类型,即)。这些是可变数据类型和 例如,包括“列表”

大多数对象没有(例如:stringintegertuple 等...)

我终于得出结论:

  1. python 总是通过引用传递!

  2. 但是,某些数据类型是不可变的(字符串、整数等)

  3. 当对那些的引用被传递给一个函数时;对这些变量的任何操作都需要对变量进行复制以存储操作结果。

  4. 变量的任何重复都会导致创建新引用以指向新值。这解释得很好here

  5. 因此,当这种情况发生时,调用函数不再看到操纵结果。这是因为没有办法重新分配调用函数的参数的引用以指向新的引用。一旦必须复制变量的内容,调用函数就不再看到它,因为新内容具有新的引用。

  6. 这意味着可以附加诸如lists 之类的可变变量并保留相同的引用 - 因此看起来好像它们是通过引用传递的。

  7. 但是像stringsintegers 这样的不可变对象将在操作后丢失它们的引用,因为必须使用该新值创建一个新对象(例如stringinteger)。新对象将永远有一个新的引用! 因此,在这些情况下,函数调用将表现就好像它已按值传递!

我在下面提供了一些代码来很好地说明这一点:

'''
The results of this block of code suggest that python passes by value.
However, in truth, the output of the code PROVES a reference is passed!
But because the variable's data type is immutable, a new object is created within the function with a NEW reference.
The upshot of this being, behaviour which seems to show that the argument is passed by value, when this is not strictly the case.
'''
# EXAMPLE 1:
class PassByReference:
    def __init__(self):
        self.variable = 1
        print("BEFORE function call, self.variable = " + str(self.variable))
        print("BEFORE function call, id(self.variable) = " + str(id(self.variable)))
        self.change(self.variable)
        print("AFTER function call, self.variable = " + str(self.variable))
        print("AFTER function call, id(self.variable) = " + str(id(self.variable)))

    def change(self, var):
        print("INSIDE modifying function, var TO BE incremented!  id(var) BEFORE incrementing = " + str(id(var)))
        var = var + 1
        print("INSIDE modifying function, var incremented!  Now, var = " + str(var))
        print("INSIDE modifying function, AFTER var incremented!  id(var) AFTER incrementing = " + str(id(var)))

p = PassByReference()

以下是证明相同发现的更多示例:

'''
The results of all the examples below suggest that python passes by value.
However, in truth, the output of each block of code PROVES a reference is passed!

Where the variable data type is immutable, a new object is created within the function with a NEW reference.  The upshot of this being, behaviour which seems to show that the argument is passed by value, when this is not strictly the case.

Where the data type is mutable, the output clearly identifies that the arguments have been passed by reference and have been successfully mutated without losing their object reference so that "By Reference" behaviour manifests.

'''

# EXAMPLE 2:
def changeListVars2(n):
    print("From inside the function, BEFORE incrementing n, id(n) = " + str(id(n)))
    n = n + 5
    print("From inside the function, n = " + str(n))
    print("From inside the function, AFTER incrementing n, id(n) = " + str(id(n)))

m = 1
print("Before function call, m = " + str(m))
print("id(m) = " + str(id(m)))


changeListVars2(m)
print("After function call, m = " + str(m))
print("id(m) = " + str(id(m)))


# EXAMPLE 3:
def changeListVars(n):
    for i in range(len(n)):
        n[i] = n[i]+5
    print("From inside the function, n = " + str(n))

x = 1
y = 2
z = 3
m = [x, y, z]
print("Before function call, m = " + str(m))
print("id(x) = " + str(id(x)))
print("id(m) = " + str(id(m)))


changeListVars(m)
print("After function call, m = " + str(m))
print("After function call, id(m) = " + str(id(m)))
print("After function call:  [x=" + str(x) + ", y=" + str(y) + ", z=" + str(z) + "]")
print("After function call, id(x) = " + str(id(x)))


print() #a new line to seperate output from the two blocks of code
print() #a new line to seperate output from the two blocks of code
print("ChangeListVars2:")


# EXAMPLE 4:
def swap(a, b):
    x = a
    print ("id(x) = " + str(id(x)))
    print ("id(a) = " + str(id(a)))
    print ("id(b) = " + str(id(b)))
    a = b

    print ("id(a) = " + str(id(a)))
    b = x
    print ("id(b) = " + str(id(b)))
    a[0]= '20'




var1 = ['1','2','3','4']
var2 = ['5','6','7','8','9']
print ("id(var1) = " + str(id(var1)))
print ("id(var2) = " + str(id(var2)))
print()

swap(var1, var2)
print()

print ("id(var1 = )" + str(id(var1)))
print ("id(var2 = )" + str(id(var2)))
print ("var1 = " + str(var1))
print ("var2 = " + str(var2))


【讨论】:

  • “因此,我可以从这段代码的输出中得出结论,python 确实通过引用传递了列表的变量,默认情况下,在传入参数时。”不,绝对不会。有关 Python 评估策略的详细讨论,请参阅链接副本
  • @juanpa.arrivillaga,如果你在我的答案顶部运行代码,它以'def changeListVars(n)'开头,并检查输出,它会建议'changeListVars(m )' 通过引用传递。函数“def changeListVars(n)”不返回结果。它只是更改了传递给它的参数,并且函数外部的 m 值仍然会被更改。这表明参数是通过引用传递的。请尝试一下。此外,您提供的链接另有建议是正确的,但存在一些差异。我正在调查这个问题,很快就会回复。
  • 不,那绝对不意味着引用调用。如果要演示引用调用,很简单。创建一个函数foo,这样对于foo(variable),变量将在调用者中重新绑定。这就是通过引用调用的方式。例如 y = 0; x = 0; foo(x); foo(y); print(x, y) 应该打印 1 1 而不是 0 0
  • 恕我直言,这不是我的代码所展示的吗?你的 foo(x) 就像我的 changeListVars(m)。你设置 x=1。我设置 m=[1] (有效)。调用 foo(x) 后,您在 foo() 之外的 x 会发生变化。调用 changeListVars(m) 后,我在 changeListVars() 之外的 m 发生了变化。
  • @IqbalHamid 试试看 n1、n2 和 n3 的树突是否更新了。
【解决方案3】:

您没有在类方法中正确地重新分配 outputDendrites 的值。

def fire(self):
    self.outputPotential = self.voltsOut
    self.firing = self.on
    print("Neuron is firing!")
    # Store the axonConnections into a temporary list for parsing since we'll be changing the values WHILE interating
    initial_axonConnections_list = self.axonConnections
    for index, outputDendrites in enumerate(initial_axonConnections_list):
        outputDendrites = self.outputPotential
        # We have stored the value in outputDendrites, but we're not doing anything with it, we have to assign it
        self.axonConnections[index] = outputDendrites

【讨论】:

  • 值得指出的是,如果您使用 IDE,您可能会立即注意到这个问题。 Pycharm 立即将“outputDendrites”标记为未使用的变量,这使我能够快速调试您的代码。我强烈建议您检查一下。
猜你喜欢
  • 2014-03-08
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-17
  • 1970-01-01
  • 2021-04-19
相关资源
最近更新 更多