2

In Python 3.4, I would like to call a child function, defined outside the parent function, that still has access to the parent function's scope (see example below). While I have named the functions parent and child in the example below for ease of viewing, the functions I am thinking of have very separate tasks and it makes more sense to define them separately. I am used to doing the following commonly in JavaScript:

def parent():
    test = 0
    child()

def child():
    test += 1
    print(test)

However, I simply get an error when executing the above code. I tried a second variation using the 'nonlocal' keyword that also failed:

def parent():
    test = 0
    child()

def child():
    nonlocal test
    test += 1
    print(test)

The error message being 'no binding for nonlocal 'test' found'. Is this possible in python as it is in many other languages or is the only option the following (not preferred):

def parent():
    test = 0

    def child():
        nonlocal test
        test += 1
        print(test)

    child()

Edit: passing the parent's variable to the child function won't work in my use case as I need to modify the parent's variable.

Edit 2: The parent and child methods are already part of a class that doesn't really logically have an attribute of a counter. The counter is something internal to the two functions to keep track of node visits in a graph (see real example below):

class Graph():
    def depthFirstSearch(self):
        for vertex in self.adjacency_list:
            vertex.status = "not visited"
            vertex.previous = None

        visit_count = 0
        for vertex in self.adjacency_list:
            if vertex.status == "not visited":
                self.depthFirstSearchVisit(vertex)


    def depthFirstSearchVisit(self, vertex):
        nonlocal visit_count
        visit_count += 1

        vertex.distance = visit_count
        vertex.status = "waiting"

        for edge in vertex.edges:
            if edge.status == "not visited":
                edge.previous = vertex
                self.depthFirstSearchVisit(edge)

        vertex.status = "visited"
        visit_count += 1
        vertex.distance_finished = visit_count
Informagician
  • 305
  • 3
  • 15
  • Use `test = child(test)` – syntonym Nov 18 '16 at 23:12
  • Thanks for the suggestion. I'm not sure if that will work as child function does more than increment a counter. The counter just keeps track of nodes visited in a graph structure. – Informagician Nov 18 '16 at 23:16
  • Use ```self.visit_count```. ```self``` is your scope for this class. – lysdexia Nov 18 '16 at 23:26
  • ok, seems I'll be adding an unusual attribute to the class (I get the reasoning from python's perspective, but it feels strange from the OOP side). Thanks for all the help! – Informagician Nov 18 '16 at 23:30
  • Everything is somewhat different under the hood, so lots of the techniques you've gottend used to from JS will not translate. On one hand, doing asynchronous things on i/o bound functions is not what you're used to. On the other hand, you'll never be in callback hell. Have fun! – lysdexia Nov 18 '16 at 23:47

2 Answers2

0

You can define a function attribute like parent.test = 0 outside the function and access it in the child function using parent.test

goldcode
  • 633
  • 8
  • 21
  • I agree, this solution also passed my mind. I guess I was hoping for something that didn't modify the class as the counter isn't logically an attribute (property) of the class. – Informagician Nov 18 '16 at 23:18
0

In this case, you'd probably like to use a class. They are really easy to get your head around. Note the self variable that is passed, and take a look here for a quick explanation of scope in classes.

#!/usr/bin/env python3

class Family(object):

    def parent(self):
        self.test = 0
        self.child()

    def child(self):
        self.test += 1
        print(self.test)

if __name__ == "__main__":
    Family().parent()

So, to translate to your code:

#!/usr/bin/env python3

class Graph(object):
    def depthFirstSearch(self):
        for vertex in self.adjacency_list:
            vertex.status = "not visited"
            vertex.previous = None

        self.visit_count = 0
        for vertex in self.adjacency_list:
            if vertex.status == "not visited":
                self.depthFirstSearchVisit(vertex)

    def depthFirstSearchVisit(self, vertex):
        self.visit_count += 1

        vertex.distance = self.visit_count
        vertex.status = "waiting"

        for edge in vertex.edges:
            if edge.status == "not visited":
                edge.previous = vertex
                self.depthFirstSearchVisit(edge)

        vertex.status = "visited"
        self.visit_count += 1
        vertex.distance_finished = self.visit_count

class Edge(object):
    status = "not vistited"

class Vertex(object):
    def __init__(self):
        self.edges = [Edge(), Edge()]

if __name__ == "__main__":
    graph = Graph()
    graph.adjacency_list = [Vertex(), Vertex()]
    graph.depthFirstSearch()

Nothing fancy needed.

Community
  • 1
  • 1
lysdexia
  • 1,786
  • 18
  • 29
  • Also, coming from the javascript world, you may want to spend some time thinking about how to structure your code so you can explicitly pass variables to other functions. ```self``` is all well and good, but you can quickly get lost when starting out. You'll be rewarded with more readable code. – lysdexia Nov 18 '16 at 23:25
  • Thanks for the advice! I have passed variables to functions in the past quite frequently as it keeps things out of global scope and makes code more flexible. Every now and then, though, the ability to access parent scope comes in super handy. – Informagician Nov 18 '16 at 23:28
  • Yep. JS drives me because I've gotten so used to python's scope behavior. With JS its' "is the scope here, or in the calling function? Does ```this``` do what I think it does?" The answer is usually "LOL PYTHON GUY" and I'm back to my copy of "The Good Parts". :-D – lysdexia Nov 18 '16 at 23:39
  • I hear you! I think JavaScript taught me the most about scope just due to the craziness involved in keeping track of everything :P :D – Informagician Nov 18 '16 at 23:43
  • why var closure is not working in python? but argument closure is working? – Yin Oct 26 '21 at 11:34