Give a class its own `self` at instantiation time? - python

I've got a button class that you can instantiate like so:
engine.createElement((0, 0), Button(code=print, args=("Stuff!",)))
And when it is clicked it will print "Stuff!". However, I need the button to destroy itself whenever it is clicked. Something like this:
engine.createElement((0, 0), Button(code=engine.killElement, args=(self,)))
However, that would just kill the caller, because self refers to the caller at that moment. What I need to do is give the class its own 'self' in advance...
I thought of just making the string 'self' refer to the self variable upon click, but what if I wanted to use the string 'self' in the arguments?
What is the way to do this? Is my architecture all wrong or something?

Unfortunately this is impossible — the arguments to the button's constructor are evaluated before the constructor is evaluated. You'd need to assign the button to a variable, then set the callback afterwards:
b = Button(code=engine.killElement)
b.args = (b, )
Or something similar.

You've essentially set it up so that you need a reference to an object in order to create that object, which is of course impossible. You could do something like this (a list is as good as a tuple for argument unpacking):
arglist = []
button = Button(code=engine.killElement, args=arglist)
arglist.append(button)
engine.createElement((0, 0), button)
This is inelegant, unclear, and verbose, but it'll get the reference to the instance into the instance.
You could use a sentinel value as another poster suggested. Perhaps a better suggestion would be to simply use a convention (like Python itself) that self is always passed as the first argument to the specified function and doesn't need to be specified explicitly. Then your callbacks are written to always take self, even if they don't do anything with it.
But generally you would not specify an object's behavior by passing it to that object's constructor, but through inheritance. In other words you'd subclass Button, override its onClick method or whatever, and instantiate the subclass. Having onClick know what instance it's attached to is a non-issue. So I come down on the side of yes, your architecture is a wee bit all wrong.

This is impossible in general.
However, if you're creating the Button class, you can pass a special sentinel value that means "yourself". For example:
class Button(object):
yourself = 'yourself'
def __init__(self, code, args):
self.code = code
self.args = [self if arg is yourself else arg for arg in args]
Then:
engine.createElement((0, 0), Button(code=engine.killElement, args=(Button.yourself,)))
Picking an appropriate sentinel can be tricky—obvious choices like None, 0, or '' may be legitimate values, and even tricky things you come up with may turn out to be useful arguments during debugging. Making yourself a class variable, or a global within the module, means that if you ever do need to redefine the sentinel, you only need to change it in one place, instead of everywhere you use it.
See http://bytes.com/topic/python/answers/555169-sentinel-values-special-cases for a brief discussion on picking an appropriate sentinel value. There's another blog out there with more information, but I haven't found it in a quick search… Anyway, here are some quick ideas:
None is always the best answer if it works.
Define an empty class to be the sentinel. Either the class object, or any instance of the class object, can be used.
Create a global instance of the object class (object()).
Define an empty function and use it (or its func_code or whatever).
Ellipsis (or type(Ellipsis), which is a type named ellipsis, but that name isn't accessible) is almost always safe, because it's only used in __getitem__ and friends (and possibly in defining slice objects to pass to them).
If there's a type that could not possibly be a valid value, and you've already got instances around, use one of those—e.g., the func_code member of the __init__ function.

Maybe something like this would help:
class Button(object):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
kwargs['args'].append(obj)
return obj
def __init__(self, code, args):
self.code = code
self.args = args
def click(self):
return self.code, self.args
b = Button(code="engine.killElement", args=[])
print b.click()
Output:
('engine.killElement', [<__main__.Button object at 0x00B59AF0>])

Related

Variable assignment in the constructor

I have a question to the variable assignment in the constructor: I have a constructer which takes the argument 'context'. First, I assign this variable to a class variable. Second, I create another class which also takes 'context' as an argument.
To my question: Is it better to assignment the class variable (self.context) or the argument from the constructor (context) to the new created class?
class State():
def __init__(self, context):
self.context = context
self.diconnected = Disconnected(self.context)
or
class State():
def __init__(self, context):
self.context = context
self.diconnected = Disconnected(context)
The end result is the same. Disconnected(context) is just slightly shorter and faster.
This is really not going to effect your program execution time in any significant way in Python. The only situation this could matter is when multiple threads may be using this data. I'd always use the argument just in case.
None is objectively "better"; you're passing the same object to Disconnected in any case.
Which one you write depends on which dependency you want to emphasize: that Disconnected has the same context as self (which also implies that self always has one), or that it has the context passed in as the parameter.

Is there a shorthand to create member variables from function arguments in python?

Suppose I have a constructor in python:
def __init__(self, bBoolFlags_a_Plenty):
self.bBoolFlags_a_Plenty = self.bBoolFlags_a_Plenty
[...] # one line for each bool flag passed in.
Is there a way to assign function arguments passed to init() to member variables/attributes of the class having the same name as the function arguments without having to hand write each one?
Of something more limited in scope, perhaps a one liner will do. Something like:
self.* = ( arg_bool1, arg_bool2, arg_bool3, arg_bool4)
Actually, I would prefer the latter, just because I don't want the 'kitchen sink' to be assigned to self.
thanks.
You can either use kwargs:
def __init__(self, **flags):
for flag, value in flags.iteritems():
setattr(self, flag, value)
Or as #bgporter correctly suggests use the __dict__ directly (assuming that you don't define the __slots__ attribute):
def __init__(self, **flags):
self.__dict__.update(flags)
Depending on what number "plenty" actually specifies, it may however be easier to keep them in a seperate dict anyway:
def __init__(self, **flags):
self.flags = flags
I'd favour the latter possibility especially if the class has other, "non-flag" attributes as well.
You could also:
def __init__(self, **kwargs):
for key in iter(kwargs):
settatr(self, key, kwargs.get(key))

How to assign instance variables as default values for keyword arguments in the same class in Python

In Python 3.3+, given a class with a method, the class has an instance variable, which is a simple object() object (we'll call it self.FALSE). How do I use that instance variable as a default value for a keyword argument?
class Test:
def __init__(self):
self.FALSE=object();
def Tester(self, key=self.FALSE):
pass;
This, of course, doesn't work because the scope of keyword argument default values does not include self (nor does it include the Test class generally). It does include global variables and at least some objects, however.
Is there a way to do this? I don't have a dire need, right now, but it would be really nice if I could do that, somehow. I would think you would be able to pass anything in as a keyword argument default value. Surely there must be a way.
Initially have the default of value key as None and in the function, if key is None, then you can set the default value, whatever you like.
def Tester(self, key=None):
if key is None:
key = self.FALSE
Also, you don't need semicolons in Python, like we need in C/C++/JAVA.
There isn't a way, because instance attributes do not exist until an instance is created, but the methods are defined when the class is created, before any instances exist. That is, saying (as you do in your question) that "the class has an instance variable" is incorrect. The class has no such thing; only the instances have that variable, and only if and when they are created.
You must do as thefourtheye suggests and handle assigning a default inside the method body.

Mutliple constructors in python calling the same routine

I am writing a class with multiple constructors using #classmethod. Now I would like both the __init__ constructor as well as the classmethod constructor call some routine of the class to set initial values before doing other stuff.
From __init__ this is usually done with self:
def __init__(self, name="", revision=None):
self._init_attributes()
def _init_attributes(self):
self.test = "hello"
From a classmethod constructor, I would call another classmethod instead, because the instance (i.e. self) is not created until I leave the classmethod with return cls(...). Now, I can call my _init_attributes() method as
#classmethod
def from_file(cls, filename=None)
cls._init_attributes()
# do other stuff like reading from file
return cls()
and this actually works (in the sense that I don't get an error and I can actually see the test attribute after executing c = Class.from_file(). However, if I understand things correctly, then this will set the attributes on the class level, not on the instance level. Hence, if I initialize an attribute with a mutable object (e.g. a list), then all instances of this class would use the same list, rather than their own instance list. Is this correct? If so, is there a way to initialize "instance" attributes in classmethods, or do I have to write the code in such a way that all the attribute initialisation is done in init?
Hmmm. Actually, while writing this: I may even have greater trouble than I thought because init will be called upon return from the classmethod, won't it? So what would be a proper way to deal with this situation?
Note: Article [1] discusses a somewhat similar problem.
Yes, you'r understanding things correctly: cls._init_attributes() will set class attributes, not instance attributes.
Meanwhile, it's up to your alternate constructor to construct and return an instance. In between constructing it and returning it, that's when you can call _init_attributes(). In other words:
#classmethod
def from_file(cls, filename=None)
obj = cls()
obj._init_attributes()
# do other stuff like reading from file
return obj
However, you're right that the only obvious way to construct and return an instance is to just call cls(), which will call __init__.
But this is easy to get around: just have the alternate constructors pass some extra argument to __init__ meaning "skip the usual initialization, I'm going to do it later". For example:
def __init__(self, name="", revision=None, _skip_default_init=False):
# blah blah
#classmethod
def from_file(cls, filename=""):
# blah blah setup
obj = cls(_skip_default_init=True)
# extra initialization work
return obj
If you want to make this less visible, you can always take **kwargs and check it inside the method body… but remember, this is Python; you can't prevent people from doing stupid things, all you can do is make it obvious that they're stupid. And the _skip_default_init should be more than enough to handle that.
If you really want to, you can override __new__ as well. Constructing an object doesn't call __init__ unless __new__ returns an instance of cls or some subclass thereof. So, you can give __new__ a flag that tells it to skip over __init__ by munging obj.__class__, then restore the __class__ yourself. This is really hacky, but could conceivably be useful.
A much cleaner solution—but for some reason even less common in Python—is to borrow the "class cluster" idea from Smalltalk/ObjC: Create a private subclass that has a different __init__ that doesn't super (or intentionally skips over its immediate base and supers from there), and then have your alternate constructor in the base class just return an instance of that subclass.
Alternatively, if the only reason you don't want to call __init__ is so you can do the exact same thing __init__ would have done… why? DRY stands for "don't repeat yourself", not "bend over backward to find ways to force yourself to repeat yourself", right?

Python 3: Giving a command to set attribute of self in __init__ with need to use "self"?

I know the question header sounds weird, but since English is not my first language, I find it very hard to formalize. However, I might be able to explain it with bit more text.
The problem is, that I'm trying to create a class called "Foo" for example.
# ../myProject/Foo.py
class Foo:
'''Represents an example class for stackoverflow'''
Now all of Foo class' instances have function attribute, which simply holds a function which can be executed via the instance. Also, there's a parameters attribute, a tuple or a list, which holds parameters which should be used when the function gets called.
def __init__(self, function, parameters):
self.function = function
self.parameters = parameters
def callFunction(self):
if self.function:
self.function(*self.parameters)
This seems to be working fine, however, the problem is, that I want to give it a default value, to change an attribute of the instance. I basically wanna do the following:
def __init__(self, function=setattr, \
parameters=(self, "togglableAttribute", not self.togglableAttribute)):
And doing this will raise NameError: name 'self' is not defined. How should I implement this in order for it to work, or should I come up with a workaround?
self is the typical variable name used to describe the instance. However, default arguments are evaluated when the function is created (at class creation time). Of course self doesn't exist yet because the class doesn't even exist yet -- The class is still in the process of being built.
The typical way to do this is to check for a sentinel;
def __init__(self, function=setattr, parameters=None):
if parameters is None:
parameters = (self, "togglableAttribute", not self.togglableAttribute)
See my answer here (and the comments below it) for a discussion of various objects that you can use as your sentinel and the various pros and cons of each.

Categories