Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Debugging with functions
#1
Hi all,

I'm working with a program that operates largely with functions. The functions are all defined at the top with the main program starting ~Line 400.

I'm having issues today with writing output to files. Yesterday it was working fine. I'm not sure what I did.

Soooo... trying to debug. One thing I want to do is add test_counter to be +=1 every time Python starts going through a function to make sure the function is actually being called. At the end of the main program, I print test_counter to see the total (hoping for a nonzero number).

At the very top of the program, I added:

test_counter = 0
How best to proceed, now, with regard to local and global variables? My thought was that since I defined this as part of the main program (not inside any functions), it will be accessible to all inside or outside functions. Unfortunately, I just got:

UnboundLocalError: local variable 'test_counter' referenced before assignment

So instead, at the top I tried:

global test_counter
test_counter = 0
Same error.

Any thoughts?

Mark
Reply
#2
Either get a development environment that supports breakpoints or learn how logging works. (preferably both).

Your problem is that test_counter += 1 will create an uninitialized local variable and then try to get the variable's value so it can add 1 and assign the result to the variable. So you are trying to use the value of test_counter before it is assigned a value.

"global test_counter" tells Python to get "test_counter" from the global scope. Putting this at the top of the file does nothing because you are already in the global scope. You need to put this line in each function that modifies the global "test_counter" variable.
test_counter = 0

def func():
    global test_counter
    test_counter += 1
This tells Python to get the "test_counter" variable assigned at the top of the module instead of creating a local variable.
Mark17 likes this post
Reply
#3
Here is a decorator that will keep a count of how many times decorated functions are called.
class FuncCounter:
    def __init__(self):
        self.counter = {}

    def observe(self, func):
        self.counter[func.__name__] = 0

        def wrapper(*args, **kwagrs):
            self.counter[func.__name__] += 1
            func(*args, **kwagrs)

        return wrapper


func_counter = FuncCounter()


@func_counter.observe
def func1():
    print("func1")


@func_counter.observe
def func2():
    print("func2")


@func_counter.observe
def func3():
    print("func3")


func1()
func1()
func2()
print(func_counter.counter)
Output:
func1 func1 func2 {'func1': 2, 'func2': 1, 'func3': 0}
Mark17 likes this post
Reply
#4
Thank you!
Reply
#5
Logging as mention and then is loguru a star ✨
Quick example also if add @logger.catch decorator just before the main function.
Loguru is now catching all errors in the main function and in all nested functions.
from loguru import logger

def func_1(x, y):
    logger.info("func_1 start")
    result = x + y
    return result

def func_2(name, email):
    logger.info("func_2 start")
    info = f'{name} {email}'
    return info

@logger.catch
def main():
    func_1(4, 9)
    func_2('kent', '[email protected]')

if __name__ == "__main__":
    main()
Output:
2021-09-24 21:00:13.457 | INFO | __main__:func_1:4 - func_1 start 2021-09-24 21:00:13.461 | INFO | __main__:func_2:9 - func_2 start
Make a error.
[Image: VQv3g8.png]
Reply
#6
(Sep-24-2021, 04:53 PM)deanhystad Wrote: "global test_counter" tells Python to get "test_counter" from the global scope. Putting this at the top of the file does nothing because you are already in the global scope. You need to put this line in each function that modifies the global "test_counter" variable.
test_counter = 0

def func():
    global test_counter
    test_counter += 1
This tells Python to get the "test_counter" variable assigned at the top of the module instead of creating a local variable.

What's the best way to share a variable calculated in one function with another function--all while being able to also access it in the main program? Maybe initialize it in the main program and then specify them as global within each function?
Reply
#7
Any variable in the module scope (global scope) can be seen by any function in the module. When Python is looking for a variable it first looks in the Local scope, followed by Enclosed scope, Global scope and Built-ins scope (LEGB). Enclosed scope occurs when you define a function inside of another function. The interior function can see the enclosing function's variables.

To set the value of a global variable from inside a function the function must declare the variable is global. If you don't declare the variable as global, variable assignment uses/creates a local variable.

Avoid using global variables. Programs with several global variables that are set from many different functions are difficult to debug. If you calculate a value in a function that is useful to other functions, the value should be returned.

This is bad
def circle(radius):
    global circle_area, circle_circumference
    circle_area = math.pi * radius**2
    circle_circmference = 2 * math.pi * radius

circle(3)
print('Area =', circle_area)
This is better
def circle(radius):
    return math.pi * radius**2, 2 * math.pi * radius

area, circumference = circle(3)
print('Area =', area)
And better yet
import math

class Circle():
    def __init__(self, radius=0):
        self.radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        self._radius = value
        self._area = math.pi * self._radius**2
        self._circumference = 2 * math.pi * self._radius

    @property
    def area(self):
        return self._area

    @area.setter
    def area(self, value):
        self.radius = (value / math.pi)**0.5

    @property
    def circumference(self):
        return self._circumference

    @circumference.setter
    def circumference(self, value):
        self.radius = value / (2 * math.pi)

    def __repr__(self):
        return f'(Circle radius={self._radius}, circumference={self._circumference}, area={self._area})'

circle = Circle(3)
print(circle)
circle.area = math.pi * 25
print(circle)
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020