'Python NameError when defining class

The below Python fails for some reason.

class NetVend:
    def blankCallback(data):
        pass

    def sendCommand(command, callback=NetVend.blankCallback):
        return NetVend.sendSignedCommand(command, NetVend.signCommand(command), callback)

    def sendSignedCommand(command, signature, callback):
        pass

I get the following error:

Traceback (most recent call last):
  File "module.py", line 1, in <module>
    class NetVend:
  File "module.py", line 5, in NetVend
    def sendCommand(command, callback=NetVend.blankCallback):
NameError: name 'NetVend' is not defined


Solution 1:[1]

Well, I wouldn't say the first, but the second option is certainly true :-)

The trouble is that the default argument is evaluated at compile time, but at that point NetVend does not exist in that scope, because (obviously) the class itself has not yet been fully evaluated.

The way round it is to set the default to None, and check within the method:

def sendCommand(command, callback=None):
   if callback is None:
        callback=NetVend.blankCallback

Solution 2:[2]

Just to give an alternative stream processing option (no need to fit input data into memory), based on convtools:

from convtools import conversion as c

# fmt: off
data = [
    {"id": 1, "phone": "123", "outcome": True, "status": "sale", "duration": 1550},
    {"id": 2, "phone": "123", "outcome": False, "status": "failed", "duration": 3},
    {"id": 3, "phone": "123", "outcome": False, "status": "no_ring", "duration": 5},
    {"id": 4, "phone": "456", "outcome": True, "status": "call_back", "duration": 550},
    {"id": 5, "phone": "456", "outcome": True, "status": "sale", "duration": 2500},
    {"id": 6, "phone": "456", "outcome": False, "status": "no_ring", "duration": 5},
    {"id": 7, "phone": "789", "outcome": False, "status": "no_pick", "duration": 4},
    {"id": 8, "phone": "741", "outcome": False, "status": "try_again", "duration": 25},
    {"id": 9, "phone": "741", "outcome": False, "status": "try_again", "duration": 10},
    {"id": 10, "phone": "741", "outcome": False, "status": "no_ring", "duration": 5},
]
# fmt: on

# you are interested in rows with max duration
max_duration_call_log = c.ReduceFuncs.MaxRow(c.item("duration"))

# you need to know whether there's been an outcome
has_outcome = c.ReduceFuncs.Count(where=c.item("outcome")) > 0

converter = (
    c.group_by(c.item("phone"))
    .aggregate(
        {
            "phone": c.item("phone"),
            "has_outcome": has_outcome,
            "status": max_duration_call_log.item("status"),
            "duration": max_duration_call_log.item("duration"),
            "outcome_record": c.if_(
                has_outcome,
                max_duration_call_log.item("id"),
                None,
            ),
        }
    )
    # this step generates and compiles ad hoc function
    .gen_converter()
)

# fmt: off
assert converter(data) == [
    {'phone': '123', 'has_outcome': True, 'status': 'sale', 'duration': 1550, 'outcome_record': 1},
    {'phone': '456', 'has_outcome': True, 'status': 'sale', 'duration': 2500, 'outcome_record': 5},
    {'phone': '789', 'has_outcome': False, 'status': 'no_pick', 'duration': 4, 'outcome_record': None},
    {'phone': '741', 'has_outcome': False, 'status': 'try_again', 'duration': 25, 'outcome_record': None},
]
# fmt: on

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Daniel Roseman
Solution 2