Home > Back-end >  Is it good practice to use function attributes?
Is it good practice to use function attributes?

Time:01-16

I've recently discovered function attributes in python. Is it good practice to use those attributes in closure and nested functions, especially changing those attributes from the inner function? Is it 'better' than using nonlocal?

For example-

def combination_lock(*args):
    combination_lock.index = 0
    code = [*args]
    combination_lock.state = True
    def inner(num):
        if code[combination_lock.index] != num:
            combination_lock.state = False
        if combination_lock.index == len(code) - 1:
            return combination_lock.state
        else:
            combination_lock.index  = 1
        return inner
    return inner
f=combination_lock(1,2,3,4)
f(1)(2)(3)(4)  # -> will return True
f(2)(1)(3)(4)  # -> will return False
f(1)(1)(1)(1)  # -> will return False

The function combination_lock receives sequence of integers, and returns a functions that gets one integer at a time (exactly the same number of integers as the sequence length), and checks if the they match the initial sequence.

As you can see, I'm using a Boolean flag that indicates whether the current int match the sequence, and index attributes that increases each call to the inner function.

Is there a more Pythonic way of doing that?

CodePudding user response:

in short the answer is NO, it's not pythonic to use function attributes, unless they are constants that won't change in the entire program, such as mathematical constants or error codes or documentation.

main problem is that your inner function is returning either a function or a boolean ... which is very confusing and will cause errors

f=combination_lock(1,2,3,4)
f(1)(2)(3)(4)  # -> will return True
f(2)(1)(3)(4)  # -> will raise error because (2) returns bool
f(1)(1)(1)(1)  # -> will raise error because (1) returns bool

second problem is mostly with the memory, as your combination_lock.index is shared between all created instances of f, which makes it hard to debug or even work correctly, which is why it's not recommended to use function attributes, unless they are constants.

what you are trying to achieve with function attributes is to store data, and that's exactly what classes are for, which you can overload the __call__ operator to get to work like a function.

and in order to separate internal state of index and state you can create an inner class to keep track of them, which will be returned when you call your main class, and the other class will handle subsequent calls.

class combination_lock: # class to hold the code

    class evaluator: # inner class to hold index and state
        def __init__(self,code):
            self.code = code
            self.index = 0
            self.state = True

        # will be called every successive time after the first.
        def __call__(self, num): 
            if self.code[self.index] != num:
                self.state = False
            if self.index == len(self.code) - 1:
                return self.state
            else:
                self.index  = 1
            return self

    def __init__(self,*args):
        self.code = [*args]

    def __call__(self,num): # will be called only the first time
        evaluator = self.evaluator(self.code)
        return evaluator(num)

f=combination_lock(1,2,3,4)
print(f(1)(2)(3)(4))  # -> will print True
print(f(2)(1)(3)(4))  # -> will print False
print(f(1)(1)(1)(1))  # -> will print False

it's a lot more code than yours but its slightly clearer and debuggable, and arguably pythonic, but it's still error prone and I'd add a ton of error handling to it, but at least it's thread safe now, so you can create a lot of combination locks on any thread and you'll be sure they all work as intended.

  •  Tags:  
  • Related