Class variable that is an instance of itself - python

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.

Related

How to get class of object being created in __init__ parameters

So I am trying to create a class with an initialization method that needs to get the type of the object being created in order to properly set the default values of the init arguments.
To give a concrete example with code, say I have the following class:
def __init__(self, foo, bar=type(self).some_class_variable, ham=type(self).some_other_class_variable):
self.foo = foo
self.bar = bar
self.ham = self.some_function(ham)
This is the functionality I am looking for, but my IDE is saying "self" is not defined which I can understand, as self has yet to be instantiated. So my question is how would I go about implementing this properly? I do not want to "hardcode" the class type in where I currently have type(self) because subclasses of this class may have their own values for some_class_variable and some_other_class_variable and I want those subclasses to use the class variable corresponding to their type.
My solution needs to work in Python 3.6 & Python 3.7 but I would really prefer to find a solution that works in all versions of Python 3.6 and later if possible.
I think that you should put it in the body, not the parameter.
def __init__(self, foo):
self.foo = foo
self.bar = type(self).some_class_variable
self.ham = self.some_function(type(self).some_other_class_variable)
EDIT:
If the values are defaults, you can do this:
default_value = 'default pls'
def __init__(self, foo, bar=default_value, ham=default_value):
self.foo = foo
if default_value == bar:
self.bar = type(self).some_class_variable
if default_value == ham:
self.ham = self.some_function(type(self).some_other_class_variable)
The class name is not bound yet, as the class has not been initialised at that point. See this answer which explains this in more depth.
A way to work-around this, albeit not that great, is to create setter methods for the variables, and to set them after an instance has been initialised, like so:
class Example:
def __init__(self, foo):
self.foo = foo
self.bar = None
self.ham = None
def set_bar(self, bar):
self.bar = bar
def set_ham(self, ham):
self.ham = ham
You can go one step further to validate the type of those attributes with a simple if statement, or through python 'typing'.

After using property decorator, python object has two very similar attributes (foo.bar and foo._bar). Is that ok?

So I'm refactoring my code to be more Pythonic - specifically I've learned that using explicit getters and setters should be replaced with #property. My case is that i have an Example class with initialized bar attribute (initialization helps me to know that user set the bar):
class Example:
def __init__(self):
self.bar = 'initializedValue'
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, b):
self._bar = b
def doIfBarWasSet():
if self.bar != 'initializedValue':
pass
else:
pass
after running foo = Example() my debugger shows that foo has two attributes: _bar and bar, both set to 'initializedValue'. Also, when I run foo.bar = 'changedValue' or foo._bar = 'changedValue', both of them are changed to 'changedValue'. Why there are two attributes? Isn't that redundant? I think I understand why there is _bar attribute - I added it in #bar.setter, but why there is bar as an string attribute? Shouldn't bar be rather a method leading to bar #property?
It's fine. Keep in mind that bar is not an instance attribute, but a class attribute. Since it has type property, it implements the descriptor protocol so that its behavior is different when accessed from an instance. If e is an instance of Example, then e.bar does not give you the instance of property assigned to Example.bar; it gives you the result of Example.bar.__get__(e, Example) (which in this case, happens to be Example.bar.fget(e), where fget is the original function decorated by #property).
In short, every instance has its own _bar attribute, but access to that attribute is mediated by the class attribute Example.bar.
It's easier to see that bar is a class attribute if you write this minimal (and sufficient, since neither the getter nor setter in this case requires a def statement) definition.
class Example:
def __init__(self):
self.bar = "initalizedValue"
bar = property(lambda self: self._bar, lambda self, b: setattr(self, '_bar', b))
or more generally
def bar_getter(self):
return self._bar
def bar_setter(self, b):
self._bar = b
class Example:
def __init__(self):
self.bar = "initalizedValue"
bar = property(bar_getter, bar_setter)

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...

Declaring class object datatypes

I have a class Foo which contains a datamember of type Bar. I can't make a generalized, "default" Bar.__init__() - the Bar object is passed into the Foo.__init__() method.
How do I tell Python that I want a datamember of this type?
class Foo:
# These are the other things I've tried, with their errors
myBar # NameError: name 'myBar' is not defined
Bar myBar # Java style: this is invalid Python syntax.
myBar = None #Assign "None", assign the real value in __init__. Doesn't work
#####
myBar = Bar(0,0,0) # Pass in "default" values.
def __init__(self, theBar):
self.myBar = theBar
def getBar(self):
return self.myBar
This works, when I pass in the "default" values as shown. However, when I call getBar, I do not get back the one I passed in in the Foo.__init__() function - I get the "default" values.
b = Bar(1,2,3)
f = Foo(b)
print f.getBar().a, f.getBar().b, f.getBar().c
This spits out 0 0 0, not 1 2 3, like I'm expecting.
If I don't bother declaring the myBar variable, I get errors in the getBar(self): method (Foo instance has no attribute 'myBar').
What's the correct way to use a custom datamember in my object?
You don't need to tell Python you are going to add a certain data member – just add it. Python is more dynamic than e.g. Java in this regard.
If bar instances are essentially immutable (meaning they are not changed in practice), you can give the default instance as default value of the __init__() parameter:
class Foo:
def __init__(self, the_bar=Bar(0,0,0)):
self.my_bar = the_bar
All Foo instances uisng the default value will share a single Bar instance. If the Bar instance might be changed, this is probably not what you want, and you should use this idiom in this case:
class Foo:
def __init__(self, the_bar=None):
if the_bar is None:
the_bar = Bar(0,0,0)
self.my_bar = the_bar
Note that you shouldn't usually write getters and setters in Python. They are just unnecessary boilerplate code slowing down your application. Since Python supports properties, you also don't need them to be future-proof.
The correct way is to do nothing other than assign it in the constructor.
class Foo:
def __init__(self, bar):
self.bar = bar
def getbar(self):
return self.bar
You definitely don't have to declare bar ahead of time.
It sounds like you want Foo.bar to default to a value if one isn't specified so you might do something like this:
class Foo:
def __init__(self, bar=None):
# one of many ways to construct a new
# default Bar if one isn't provided:
self._bar = bar if bar else Bar(...)
#property
def bar(self):
"""
This isn't necessary but you can provide proper getters and setters
if you prefer.
"""
return self._bar
#bar.setter
def bar(self, newbar):
"""
Example of defining a setter
"""
return self._bar = newbar
Typically just naming the variable appropriately and omitting the setter is considered more more 'pythonic'.
class Foo:
def __init__(self, bar=None):
self.bar = bar if bar else Bar(...)
You don't declare variables in Python, and variables are untyped.
Just do:
class Foo(object):
def __init__(self, bar):
self.bar = bar
def getbar(self):
return self.bar
I suspect that the issue is caused by you using old-style classes, which are kind of odd. If you inherit from object, you get a new-style class, which is designed to be much less surprising.

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

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

Categories

Resources