0

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.

Phoenix
  • 952
  • 2
  • 12
  • 1
    Argument defaults are evaluated when the function is defined, not when it's called. `self` doesn't exist yet at definition time. – Barmar Dec 27 '21 at 05:35
  • 3
    Use `bar=None` in the parameter list, and then use `if not bar: bar = type(self).some_class_variable` in the function body. – Barmar Dec 27 '21 at 05:37
  • 1
    There is currently [a PEP](https://www.python.org/dev/peps/pep-0671/) under consideration (it may or may not actually get implemented) to handle cases like this, but I agree with @Barmar that for the time being, using a sentinel like None is best. – Dennis Dec 27 '21 at 05:43
  • @Barmar Okay thank you – Phoenix Dec 27 '21 at 06:09
  • @Dennis Good to know, thanks – Phoenix Dec 27 '21 at 06:10
  • Why do you need to call `type(self)`? Instances inherit class variables, so you can just use `self.some_class_variable` – Barmar Dec 27 '21 at 06:13
  • @Barmar I tried that and also saw an error stating `"self" is not defined` – Phoenix Dec 27 '21 at 06:44
  • In the body of the function or the argument list? The latter fails for the same reason `type(self)` fails -- the default value is evaluated when the funciton is defined, not when it's called. – Barmar Dec 27 '21 at 06:45
  • @Barmar Sorry I thought you meant to do that in the function argument list I misunderstood. – Phoenix Dec 27 '21 at 06:47

2 Answers2

1

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)
  • This solution would not work for me because it does not provide a way for the caller to supply values for bar and ham, I only wanted to create defaults in case of no caller value – Phoenix Dec 27 '21 at 06:08
  • @Phoenix You do that the same way you provide call-time defaults for any other parameter. – Barmar Dec 27 '21 at 06:10
0

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