How can I access "static" class variables within methods? - python

If I have the following code:
class Foo(object):
bar = 1
def bah(self):
print(bar)
f = Foo()
f.bah()
It complains
NameError: global name 'bar' is not defined
How can I access class/static variable bar within method bah?

Instead of bar use self.bar or Foo.bar. Assigning to Foo.bar will create a static variable, and assigning to self.bar will create an instance variable.

Define class method:
class Foo(object):
bar = 1
#classmethod
def bah(cls):
print cls.bar
Now if bah() has to be instance method (i.e. have access to self), you can still directly access the class variable.
class Foo(object):
bar = 1
def bah(self):
print self.bar

As with all good examples, you've simplified what you're actually trying to do. This is good, but it is worth noting that python has a lot of flexibility when it comes to class versus instance variables. The same can be said of methods. For a good list of possibilities, I recommend reading Michael Fötsch' new-style classes introduction, especially sections 2 through 6.
One thing that takes a lot of work to remember when getting started is that python is not java. More than just a cliche. In java, an entire class is compiled, making the namespace resolution real simple: any variables declared outside a method (anywhere) are instance (or, if static, class) variables and are implicitly accessible within methods.
With python, the grand rule of thumb is that there are three namespaces that are searched, in order, for variables:
The function/method
The current module
Builtins
{begin pedagogy}
There are limited exceptions to this. The main one that occurs to me is that, when a class definition is being loaded, the class definition is its own implicit namespace. But this lasts only as long as the module is being loaded, and is entirely bypassed when within a method. Thus:
>>> class A(object):
foo = 'foo'
bar = foo
>>> A.foo
'foo'
>>> A.bar
'foo'
but:
>>> class B(object):
foo = 'foo'
def get_foo():
return foo
bar = get_foo()
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
class B(object):
File "<pyshell#11>", line 5, in B
bar = get_foo()
File "<pyshell#11>", line 4, in get_foo
return foo
NameError: global name 'foo' is not defined
{end pedagogy}
In the end, the thing to remember is that you do have access to any of the variables you want to access, but probably not implicitly. If your goals are simple and straightforward, then going for Foo.bar or self.bar will probably be sufficient. If your example is getting more complicated, or you want to do fancy things like inheritance (you can inherit static/class methods!), or the idea of referring to the name of your class within the class itself seems wrong to you, check out the intro I linked.

class Foo(object):
bar = 1
def bah(self):
print Foo.bar
f = Foo()
f.bah()

bar is your static variable and you can access it using Foo.bar.
Basically, you need to qualify your static variable with Class name.

You can access class variables by object and directly by class name from the outside or inside of class and basically, you should access class variables directly by class name because if there are the same name class and instance variables, the same name instance variable is prioritized while the same name instance variable is ignored when accessed by object. So, using class name is safer than using object to access class variables.
For example, you can access the class variable by object and directly by class name from the outside of the class as shown below:
class Person:
name = "John" # Class variable
obj = Person()
print(obj.name) # By object
print(Person.name) # By class name
Output:
John
John
But, if you add the same name instance variable as the class variable by object:
class Person:
name = "John" # Class variable
obj = Person()
obj.name = "Tom" # Adds the same name instance variable as class variable
print(obj.name) # By object
print(Person.name) # By class name
Or, if you add the same name instance variable as the class variable by self in __init__():
class Person:
name = "John" # Class variable
def __init__(self, name):
self.name = name # Adds the same name instance variable as class variable
obj = Person("Tom")
print(obj.name) # By object
print(Person.name) # By class name
The same name instance variable is prioritized when accessed by object:
Tom # By object
John # By class name
And, you can also access the class variable by self and directly by class name from the inside of the instance method as shown below:
class Person:
name = "John" # Class variable
def test(self): # Instance method
print(self.name) # By "self"
print(Person.name) # By class name
obj = Person()
obj.test()
Output:
John
John
But, if you add the same name instance variable as the class variable by object:
class Person:
name = "John" # Class variable
def test(self): # Instance method
print(self.name) # By "self"
print(Person.name) # By class name
obj = Person()
obj.name = "Tom" # Adds the same name instance variable as the class variable
obj.test()
Or, if you add the same name instance variable as the class variable by self in __init__():
class Person:
name = "John" # Class variable
def __init__(self, name):
self.name = name # Adds the same name instance variable as the class variable
def test(self): # Instance method
print(self.name) # By "self"
print(Person.name) # Directly by class name
obj = Person("Tom")
obj.test()
The same name instance variable is prioritized when accessed by self:
Tom # By "self"
John # By class name

Related

Why can't python third-level nested classes inherit from each other?

This code is valid in python:
class Inner:
class InnerInner:
attr = 1
class Inner2(Inner):
class InnerInner2(Inner.InnerInner):
attr = 2
Why is the following not possible in python?
class Outer:
class Inner:
class InnerInner:
attr = 1
class Inner2(Inner):
class InnerInner2(Inner.InnerInner):
attr = 2
It gives me following error:
class InnerInner2(Inner.InnerInner):
NameError: name 'Inner' is not defined
I would expect that Inner would be completely defined before definition of Inner2 starts.
Same reason this fails:
class Foo:
a = 1
def bar(self):
return a
Foo().bar()
Just like code inside bar can't see anything inside Foo's scope, code inside Inner2 can't see anything from Outer's scope.
Inner is completely defined by the point you try to use it, but it's in a scope you can't access.

Class variable that is an instance of itself

Is something like this possible?
class Foo:
BAR = Foo("bar")
def __init__(self, name):
self.name = name
Currently this yields NameError: name 'Foo' is not defined.
No. annotations only applies to variable and function annotations. Until the class statement as been completely executed, there is no class Foo to instantiate. You must wait until after Foo is defined to create an instance of it.
class Foo:
def __init__(self, name):
self.name = name
Foo.BAR = Foo("bar")
You can always initialize BAR = None, then change the value of the attribute after the class is defined.
class Foo:
BAR = None # To be a Foo instance once Foo is defined
...
Foo.BAR = Foo("bar") # Fulfilling our earlier promise
That might be desirable for documentation purposes, to make it clearer in the definition that Foo.BAR will exist, though with a different value. I can't think of a situation where that would be necessary, though.

Static class property pointing to special instance of the same class in Python

Coming from cpp/c#, how does one refer to the same class in the class body in Python:
class Foo(object):
ANSWER = Foo(42)
FAIL = Foo(-1)
def __init__(self, value):
self._v = value
When I try to use this code, I get "name 'Foo' is not defined" exception in a line trying to instantiate the ANSWER instance.
The name Foo is not set until the full class body has been executed. The only way you can do what you want is to add attributes to the class after the class statement has completed:
class Foo(object):
def __init__(self, value):
self._v = value
Foo.ANSWER = Foo(42)
Foo.FAIL = Foo(-1)
It sounds like you are re-inventing Python's enum module; it lets you define a class with constants that are really instances of that class:
from enum import Enum
class Foo(Enum):
ANSWER = 42
FAIL = -1
After that class statement has run, Foo.ANSWER is an instance of Foo with a .value attribute set to 42.

Re-defining already assigned python class

I am new to Python and I inherited someone's code that had the following code structure. Why do I get an object not callable and how can I redefine this method again even after re-assigning l.bar. Another question would therefore be what's the difference between l.bar and l.bar()?
>>> class foo(object):
... def __init__(self):
... self.name = "Food"
... class bar(object):
... def __init__(self):
... self.name = "Is"
... class tea(object):
... def __init__(self):
... self.name = "Good"
...
>>> l = foo()
>>> m = l.bar()
>>> m.name = "Was"
>>> l.bar = m
>>> r = l.bar()
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'bar' object is not callable
As others have pointed out, it's generally not good practice to have nested classes. But, here's a breakdown of what's happening:
class foo(object):
def __init__(self):
self.name = "Food"
class bar(object):
def __init__(self):
self.name = "Is"
class tea(object):
def __init__(self):
self.name = "Good"
l = foo() # l is now an instance of foo
print l.name # "Food"
m = l.bar() # m is now an instance of bar
print m.name # "Is"
m.name = "Was" # you've assigned m's name to "Was"
print m.name # "Was"
l.bar = m # you are overriding foo's nested bar class now with an instance of bar
print l.name # "Food"
print l.bar # <__main__.bar object at 0x108371ad0>: this is now an instance, not a class
print l.bar.name # "Was"
r = l.bar() # you are now trying to call an instance of bar
The last line doesn't work because of the same reasons calling l() or foo()() doesn't work.
If you absolutely must figure out a way to make foo.bar().name return something else, you can create a new class and reassign foo.bar to it. But, this is really gross and not recommended. Hopefully, you can just change that original code.
print foo.bar().name # "Is"
class NewBar(object):
def __init__(self):
self.name = 'Was'
foo.bar = NewBar
print foo.bar().name # "Was"
Why do i get an object not callable
You assigned l.bar to be an instance of the class foo.bar (specifically, you assigned m to it). Instances of that class aren't callable, therefore l.bar isn't callable.
how can i redefine this method again even after re-assigning l.bar
Maybe this advice is too obvious, but don't re-assign l.bar.
However, you can reset l.bar so that it refers to the method it originally referred to, by doing del l.bar.
The reason this works is because if the individual object has no bar attribute of its own, then Python looks next to see whether its class has an attribute of the same name. So, to begin with the expression l.bar evaluates to the class foo.bar, since l has type foo. Then you assigned l a bar attribute of its own, so l.bar suddenly starts evaluating to that object instead. You can restore normality by deleting the object's own attribute.
what's the difference between l.bar and l.bar()
l.bar just gets the value of the attribute bar from the object l (or from its class, if the object l doesn't have one of its own, as explained above. If that fails too it'd go to base classes). l.bar() gets the value of that attribute and then calls it. () at this position means a function call, so the thing you put it after had better be callable.
It is not clear which of the following problems you are experiencing:
1. indentation issue
When copy-pasting from source to terminal, indentation sometimes gets messed up. in ipython you can use %paste to safely paste code.
The correctly indented class declarations are:
class foo(object):
def __init__(self):
self.name = "Food"
class bar(object):
def __init__(self):
self.name = "Is"
class tea(object):
def __init__(self):
self.name = "Good"
But then the other commands make no sense.
2. instance is not the same as class
When defining a class inside a class, you have to use the outer class name to "get" to the inner class name. I.e.:
class foo(object):
def __init__(self):
self.name = "Food"
class bar(object):
def __init__(self):
self.name = "Is"
class tea(object):
def __init__(self):
self.name = "Good"
foo_inst = foo()
bar_inst = foo.bar()
tea_inst = foo.bar.tea()
Anyhow, these lines still make not much sense:
>>> l.bar = m
>>> r = l.bar()
Why would you want to override bar which is (was) a class name...

How to add or change a Class (not an instance)?

I would like to set an attribute to an class object directly, without creating an instance, e.g. having an alternative name that can be accessed like the __ name __ attribute:
class Foo:
pass
> Foo.__name__
Foo
But this doesn't work:
some_file.py:
class Foo:
alternativ_name = __name__ + "_ending"
print(Foo.alternativ_name)
This prints:
__main___ending
If I try it in the interactive Console, it returns something else again:
>>> class Foo:
... alt_name = __name__ + "_ending"
...
>>> Foo.alt_name
'builtins_ending'
What I would like to achive is:
class Foo:
alt_name = __name__ + "_ending"
Foo.alt_name
should return:
'Foo_ending'
How do I do this?
The variables __name__ and Foo.__name__ actually point to two different things. Using __name__ within the Foo class still uses the global variable, and not Foo.__name__.
Within the class, it is not possible to explicitly reference the same class:
class Foo:
alt_name = Foo.__name__ + "_ending"
# raises NameError: name 'Foo' is not defined
If you want the property on objects, you can do it during runtime, e.g. in the __init__. If you really want the property on the class itself, you can do that using metaclasses:
class Foo:
class __metaclass__(type):
#property
def alt_name(cls):
return cls.__name__ + "_ending"
Foo.__name__ has not yet been created at the point you are trying to access it. Therefore, when you access __name__ it gets the module's __name__. There are several ways you can solve this. One is by using a metaclass, but this is pretty overkill for just adding an attribute to a class. The second is to use a decorator on the class, and the third is to make alt_name a non-data descriptor or maybe a property.
Using a decorator:
def add_alt_name(template):
def decorator(klass):
klass.alt_name = template.format(klass.__name__)
return klass
return decorator
#add_alt_name(template="{}_ending")
class Foo:
pass
print(Foo.alt_name)
Using a non-data descriptor:
class AlternativeName:
def __init__(self, template, name="alt_name"):
self.template = template
self.name = "_" + name
def __get__(self, instance, klass):
try:
return getattr(klass, self.name)
except AttributeError:
pass
alt_name = self.template.format(klass.__name__)
setattr(klass, self.name, alt_name)
return alt_name
class Foo:
alt_name = AlternativeName(template="{}_ending")
print(Foo.alt_name)
Much simpler just to use a decorator.

Categories

Resources