2

I'm trying to use a static class attribute to define another static class attribute, which I thought could be achieve by the following code:

f = lambda s: s[::-1]
class A:
    foo = "foo"
    bar = f(A.foo)

However, this results in NameError: name 'A' is not defined. I found an explanation of why here, but this doesn't explain how one is supposed to work around this. So how could one define A.bar == "oof"?

Frank Vel
  • 1,202
  • 1
  • 13
  • 27

3 Answers3

0

Since you are calling f in the scope of the class, just pass foo:

f = lambda s: s[::-1]
class A:
  foo = 'foo'
  bar = f(foo)

print(A.bar)

Output:

oof
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
0

The way to do this is:

class A:
    foo = "foo"
    bar = f(foo)

A class definition is a lot like a function body. It's evaluated once to define the class, rather than evaluated each time a function is called, but either way, any variables you define locally in that scope can be used in that scope.

So, foo is a local variable within the code that defines the class. Where it gets confusing is that, for code that runs after the class is created, the local variable no longer exists, and instead foo is an attribute of the class:

class A:
    foo = "foo"
    bar = f(foo) # this works
    bar = f(A.foo) # this doesn't, because there is no A yet
    def spam(self):
        print(A.foo) # this works
        print(self.foo) # so does this
        print(type(self).foo) # and this
        print(foo) # this is an error, because the "local" foo no longer exists
abarnert
  • 354,177
  • 51
  • 601
  • 671
0

You generally can't reference a class by its name in its own definition because it hasn't been created yet. One way to (sometimes) workaround that limitation is by using what is know as a class decorator (see PEP 3129), which is a function that modifies a class after it's created.

Here's a good tutorial I found about them and how they work.

Since we want to pass arguments to the decorator, there's a need for an additional function that will act as what you could call a "decorator factory" that generates the needed class decorator on-the-fly based on the arguments passed to it.

Here's what I mean:

def decorate(func, arg):
    def decorator(cls):
        setattr(cls, 'foo', arg)
        setattr(cls, 'bar', func(arg))
        return cls

    return decorator

f = lambda s: s[::-1]

@decorate(f, 'foo')
class A:
    pass

print(A.foo)  # -> foo
print(A.bar)  # -> oof

Here's another way that also uses Python's decorator syntax, but in a slightly unconventional way in the sense that it's being used to modify a class attribute that would normally have become a method in such a way that it's value will instead be the result of calling some specified function with the argument given:

def call(*argv, **kwargs):
    def call_fn(fn):
        return fn(*argv, **kwargs)
    return call_fn

f = lambda s: s[::-1]

class A:
    foo = 'foo'
    @call(f, foo)  # Makes bar become the result of calling f with the argument.
    def bar(func, arg):
        return func(arg)


print(A.foo)  # -> foo
print(A.bar)  # -> oof
martineau
  • 119,623
  • 25
  • 170
  • 301