I have a list of settings defaults held within my init function. These defaults are all instance variables. For example,
self.set_acqmode = 1
self.set_readmode = 4
self.set_triggermode = 0
I have a function within this class which I want to use to change these default settings by only passing in a name and a value as arguments. For example,
def change_setting(self, setting, *arg):
What would be a pythonic way of accessing and changing the correct instance variable. I have tried using both vars() and dict to view these variables but the former only showed the the classes functions and the latter needs to refer to a instance of the class (which won't exist yet as this is within the class).
(If there is a better way of doing this without searching for the variables I would still like to know how to view them, just out of interest.)
Use setattr:
def change_setting(self, setting, *arg):
setattr(self, setting, arg)
setattr will work. But you have to ask, if you're going through all this trouble to rewrite setattr, are you even doing this the right way?
If you just want to have some arbitrary keys & values - well, that's a dictionary, and you should use it as such. override __getitem__/__setitem__ if you need custom behaviour.
if you really need attributes, then there's no reason a parent function wouldn't just do
myobj.set_triggermode = value
rather than the overly complex
myobj.change_setting('triggermode', value)
so you should do that. and even if you want to do that for some reason - use kwargs instead of args, probably closer to what you want.
You can certainly use __dict__:
>>> class Test(object):
def __init__(self):
self.x = 1
def set(self, attr, val):
self.__dict__[attr] = val
>>> a = Test()
>>> a.set('x', 2)
>>> a.x
2
You can use __dict__ on the name of a class without having instantiated an object of that class. For example:
print myClass.__dict__.keys()
::edit:: Of course, if you're being rigorous in your programming, you may consider using __slots__, in which case you will have predefined all the instance variables of your class by yourself.
Related
I'm trying to implement an (admittedly unPythonic) way of encapsulating a lot of instance variables.
I have these variables' names mapped to the respective values inside a dictionary, so instead of writing a lot of boilerplate (i.e. self.var = val, like times 50), I'm iterating over the dictionary while calling __setattr__(), this way:
class MyClass:
__slots__ = ("var1", "var2", "var3")
def __init__(self, data):
for k, v in data.items():
self.__setattr__(k, v)
Then I would override __setattr__() in a way that controls access to these properties.
From within __setattr__(), I'd check if the object has the property first, in order to allow setattr calls inside __init__():
def __setattr__(self, k, v):
if k in self.__class__.__slots__:
if hasattr(self, k):
return print("Read-only property")
super().__setattr__(k, v)
The problem is, I also need some of these properties to be writeable elsewhere in myClass, even if they were already initialized in __init__(). So I'm looking for some way to determine if setattr was called inside the class scope or outside of it, e.g.:
class MyClass:
__slots__ = ("var",)
def __init__(self):
self.__setattr__("var", 0)
def increase_val(self):
self.var += 1 # THIS SHOULD BE ALLOWED
my_obj = MyClass()
my_obj.var += 1 # THIS SHOULD BE FORBIDDEN
My pseudo definition would be like:
# pseudocode
def setattr:
if attribute in slots and scope(setattr) != MyClass:
return print("Read-only property")
super().setattr
Also, I'd rather not store the entire dictionary in one instance variable, as I need properties to be immutable.
Answering my own question to share with anyone with the same issue.
Thanks to #DeepSpace in the comments I've delved a bit into the frame inspection topic which I totally ignored before.
Since the well known inspect library relies on sys._getframe() in some parts, namely the parts that I'm mainly interested in, I've decided to use sys instead.
The function returns the current frame object in the execution stack, which is equipped with some useful properties.
E.g., f_back allows you to locate the immediate outer frame, which in case __setattr__() was called within the class, is the class itself.
On the outer frame, f_locals returns a dictionary with the variables in the frame's local scope and their respective values.
One can look for self inside f_locals to determine wether the context is a class, although it's a bit 'dirty' since any non-class context could have a self variable too.
However, if self is mapped to an object of type MyClass, then there shouldn't be ambiguities.
Here's my final definition of __setattr__()
def __setattr__(self, k, v):
if k in self.__class__.__slots__:
self_object = sys._getframe(1).f_back.f_locals.get("self")
if self_object is None or self_object.__class__ != MyClass:
return print(k, "is a read-only property")
super().__setattr__(k, v)
As a conclusion, I feel like pursuing variable privacy in Python is kind of going against the tide; it's definitely a cleaner solution to label variables as 'protected' according to the recognized standard, without bothering too much about the actual accessibility.
Another side note is that frame inspection doesn't look like a very reliable approach for applications meant for production, but more like a debugging tool. As a matter of fact, some inspect functions do not work with some Python implementations, e.g. those lacking stack frame support.
I want to be able to change the reference of a variable within the class Test
class Test():
def change(self, Other_Class):
self.__class__ = Other_Class.__class__
self = Other
class Other():
def set_data(self, data):
self.data = data
def one(self):
print('foo')
a = Test()
b = Other()
b.set_data([1,2,3])
a.change(b)
a.data
AttributeError: 'Other' object has no attribute 'data'
How can I change the reference to a to be what ever variable I pass through to Test().change
I would like this to work for builtin datatypes as well, but I get a different error for that.
what would be the best way to do this?
Inside Test.change, that self is a parameter, and parameters are just local variables.
And rebinding local variables doesn't have any effect on anything outside of the function.
In particular, it has no effect on any other variables (or list elements, or attributes of other objects, etc.), like the global a, that were also bound to the same value. They remain names for that same value.
It's not even clear what you're trying to do here. You change the type of a into the type of b, and that works. But what else do you want to do?
Do you want to change a into the object b, with the same identity? If so, you don't need any methods for that; that's what a = b means. Or do you want to be a distinct instance, but share an instance __dict__? Or to copy all of b's attributes into a? Shallow or deep? Should any extra attributes a had lying around be removed as well? Do you only care about attributes stored in the __dict__, or do you need, e.g., __slots__ to work?
Anyway, something that might be reasonable, for some strange use case, is this:
def change(self, other):
inherited = dict(inspect.getmembers(self.__class__))
for name, value in inspect.getmembers(self):
if name not in inherited and not name.startswith('__'):
delattr(self, name)
self.__class__ = other.__class__
inherited = dict(inspect.getmembers(other.__class__))
for name, value in inspect.getmembers(other):
if name not in inherited and not name.startswith('__'):
setattr(self, name, value)
Whether that's useful for your use case, I have no idea. But maybe it gives you an idea of the kinds of things you can actually do with the Python data model.
So if I have an class like this:
class A:
def __init__(self):
self.a = 1
obj = A()
obj.b = 2
Since I need to write a __setattr__ method to modify the attributes (ex. if it was defined inside __init__ then do something; if it was defined outside __init__ do something else. How do I determine if it was declared in init or not?
def __setattr__(self,name,value):
if name not in self.__dict__:
self.__dict__['ABC'+ name] = value # add 'ABC' before attribute's name if it was declared in __init__
else:
self.__dict__[name] = value # if it was declared outside __init__ then the attribute name doesn't change
Most of the instance attributes that you define or the parent class (or object) does are going to behave the same and be for the most part indistinguishable. If you really want to distinguish them for whatever reason, you should yourself create a way to identify them, perhaps by using a dictionary instead.
class A:
def __init__(self):
self.my_variables = {'a': 1}
# Or maintain a list with their names, it seems ugly however
self.my_variables = ['a']
With that said, I am not at all clear about why you want to do this. Maybe you should try looking for a simpler solution to the problem than overriding __setattr__.
Update:
It seems to me that you're trying to restrict updation of variables, perhaps in your attempt to create "real private variables". In my advice, don't. There's a reason that Python allows you to do a lot of things that might seem insane from point of view of Static languages. You should just start your variables with _ to mark them as private similar to what Python recommends. If people are going to access them anyway, then what's stopping them from finding a workaround to circumvent the "restrictions" that you're trying to enforce? Besides, sometimes there is a genuine justification for accessing private variables.
I have class with custom getter, so I have situations when I need to use my custom getter, and situations when I need to use default.
So consider following.
If I call method of object c in this way:
c.somePyClassProp
In that case I need to call custom getter, and getter will return int value, not Python object.
But if I call method on this way:
c.somePyClassProp.getAttributes()
In this case I need to use default setter, and first return need to be Python object, and then we need to call getAttributes method of returned python object (from c.somePyClassProp).
Note that somePyClassProp is actually property of class which is another Python class instance.
So, is there any way in Python on which we can know whether some other methods will be called after first method call?
No. c.someMethod is a self-contained expression; its evaluation cannot be influenced by the context in which the result will be used. If it were possible to achieve what you want, this would be the result:
x = c.someMethod
c.someMethod.getAttributes() # Works!
x.getAttributes() # AttributeError!
This would be confusing as hell.
Don't try to make c.someMethod behave differently depending on what will be done with it, and if possible, don't make c.someMethod a method call at all. People will expect c.someMethod to return a bound method object that can then be called to execute the method; just define the method the usual way and call it with c.someMethod().
You don't want to return different values based on which attribute is accessed next, you want to return an int-like object that also has the required attribute on it. To do this, we create a subclass of int that has a getAttributes() method. An instance of this class, of course, needs to know what object it is "bound" to, that is, what object its getAttributes() method should refer to, so we'll add this to the constructor.
class bound_int(int):
def __new__(cls, value, obj):
val = int.__new__(cls, value)
val.obj = obj
return val
def getAttributes(self):
return self.obj.somePyClassProp
Now in your getter for c.somePyClassProp, instead of returning an integer, you return a bound_int and pass it a reference to the object its getAttributes() method needs to know about (here I'll just have it refer to self, the object it's being returned from):
#property
def somePyClassProp(self):
return bound_int(42, self)
This way, if you use c.somePyPclassProp as an int, it acts just like any other int, because it is one, but if you want to further call getAttributes() on it, you can do that, too. It's the same value in both cases; it just has been built to fulfill both purposes. This approach can be adapted to pretty much any problem of this type.
It looks like you want two ways to get the property depending on what you want to do with it. I don't think there's any inherent Pythonic way to implement this, and you therefore need to store a variable or property name for each case. Maybe:
c.somePyClassProp
can be used in the __get__ and
c.somePyClassProp__getAttributes()
can be implemented in a more custom way inside the __getattribute__ function.
One way I've used (which is probably not the best) is to check for that exact variable name:
def __getattribute__(self, var_name):
if ('__' in var_name):
var_name, method = var_name.split('__')
return object.__getattribute__(self, var_name).__getattribute__(method)
Using object.__get__(self, var_name) uses the object class's method of getting a property directly.
You can store the contained python object as a variable and the create getters via the #property dectorator for whatever values you want. When you want to read the int, reference the property. When you want the contained object, use its variable name instead.
class SomePyClass(object):
def getInt(self):
return 1
def getAttributes(self):
return 'a b c'
class MyClass(object):
def __init__(self, py_class):
self._py_class = py_class
#property
def some_property(self):
return self._py_class.getInt()
x = MyClass(SomePyClass())
y = self.some_property
x._py_class.getAttributes()
These both work:
class A:
V = 3
def getV(self):
return self.V
def getVbis(self):
return A.V
print A().getV()
print A().getVbis()
Which one is more pythonic? Why?
self.V contains the value of an instance variable, while A.V contains the value of a class variable. Depending on what your class methods do to V and how they do it, getV and getVbis will return different things.
Here's an example:
class A:
V = 3
def getV(self):
return self.V
def getVbis(self):
return A.V
def setV(self, newVal):
self.V = newVal
aInst = A()
print aInst.getV()
print aInst.getVbis()
aInst.setV(5)
print aInst.getV()
print aInst.getVbis()
The above code will result in the following:
3
3
5
3
So I don't think this is about which one is more Pythonic. Rather, it's about what you're trying to do with your class variables.
In spite of the actual meaning of your code, I think the getter/setter way is NOT that pythonic.
First the variable is by default public. The getter func makes it more complex;
Second if you want to have some constraint or some other logic in the getter/setter func, it should have a more obvious name which indicate the logic. The name getXXX means nothing.
Btw, if you really don't want to access the variable directly, there is another option: http://docs.python.org/2/library/functions.html#property
I myself do prefer to access class members (attributes and methods) as instance methods within instance methods. i do not know if it is more Pythonic or not, but it allows me to focus on interfaces, because I can always override the class member with an instance member. The class member in this case provides some useful defaut (value, or implementation).
It might not be pretty, but I prefer:
self.__class__.V
This way you don't have to explicitly refer to the class name, which makes subclassing easier, and there is no danger of accidentally getting an instance attribute rather than a class attribute.