Best way to access a class variable in an object? - python

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.

Related

How to determine if a method was called from within the class where it's defined?

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.

Python promise class turn to the resolved value [duplicate]

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.

Change class variable reference within the class

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.

How to view the instance variables of a class

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.

Python properties as instance attributes

I am trying to write a class with dynamic properties. Consider the following class with two read-only properties:
class Monster(object):
def __init__(self,color,has_fur):
self._color = color
self._has_fur = has_fur
#property
def color(self): return self._color
#property
def has_fur(self): return self._has_fur
I want to generalize this so that __init__ can take an arbitrary dictionary and create read-only properties from each item in the dictionary. I could do that like this:
class Monster2(object):
def __init__(self,traits):
self._traits = traits
for key,value in traits.iteritems():
setattr(self.__class__,key,property(lambda self,key=key: self._traits[key]))
However, this has a serious drawback: every time I create a new instance of Monster, I am actually modifying the Monster class. Instead of creating properties for my new Monster instance, I am effectively adding properties to all instances of Monster. To see this:
>>> hasattr(Monster2,"height")
False
>>> hasattr(Monster2,"has_claws")
False
>>> blue_monster = Monster2({"height":4.3,"color":"blue"})
>>> hasattr(Monster2,"height")
True
>>> hasattr(Monster2,"has_claws")
False
>>> red_monster = Monster2({"color":"red","has_claws":True})
>>> hasattr(Monster2,"height")
True
>>> hasattr(Monster2,"has_claws")
True
This of course makes sense, since I explicitly added the properties as class attributes with setattr(self.__class__,key,property(lambda self,key=key: self._traits[key])). What I need here instead are properties that can be added to the instance. (i.e. "instance properties"). Unfortunately, according to everything I have read and tried, properties are always class attributes, not instance attributes. For example, this doesn't work:
class Monster3(object):
def __init__(self,traits):
self._traits = traits
for key,value in traits.iteritems():
self.__dict__[key] = property(lambda self,key=key: self._traits[key])
>>> green_monster = Monster3({"color":"green"})
>>> green_monster.color
<property object at 0x028FDAB0>
So my question is this: do "instance properties" exist? If not, what is the reason? I have been able to find lots about how properties are used in Python, but precious little about how they are implemented. If "instance properties" don't make sense, I would like to understand why.
No, there is no such thing as per-instance properties; like all descriptors, properties are always looked up on the class. See the descriptor HOWTO for exactly how that works.
You can implement dynamic attributes using a __getattr__ hook instead, which can check for instance attributes dynamically:
class Monster(object):
def __init__(self, traits):
self._traits = traits
def __getattr__(self, name):
if name in self._traits:
return self._traits[name]
raise AttributeError(name)
These attributes are not really dynamic though; you could just set these directly on the instance:
class Monster(object):
def __init__(self, traits):
self.__dict__.update(traits)
So my question is this: do "instance properties" exist?
No.
If not, what is the reason?
Because properties are implemented as descriptors. And the magic of descriptors is that they do different things when found in an object's type's dictionary than when found in the object's dictionary.
I have been able to find lots about how properties are used in Python, but precious little about how they are implemented.
Read the Descriptor HowTo Guide linked above.
So, is there a way you could do this?
Well, yes, if you're willing to rethink the design a little.
For your case, all you want to do is use _traits in place of __dict__, and you're generating useless getter functions dynamically, so you could replace the whole thing with a couple of lines of code, as in Martijn Pieters's answer.
Or, if you want to redirect .foo to ._foo iff foo is in a list (or, better, set) of _traits, that's just as easy:
def __getattr__(self, name):
if name in self._traits:
return getattr(self, '_' + name)
raise AttributeError
But let's say you actually had some kind of use for getter functions—each attribute actually needs some code to generate the value, which you've wrapped up in a function, and stored in _traits. In that case:
def __getattr__(self, name):
getter = self._traits.get(name)
if getter:
return getter()
raise AttributeError
What I need here instead are properties that can be added to the instance.
A property() is a descriptor and those only work when stored in classes, not when stored in instances.
An easy way to achieve the effect of an instance property is do def __getattr__. That will let you control the behavior for lookups.
In case you don't need to make that properties read-only - you can just update object __dict__ with kwargs
class Monster(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
than you can make instances of that class like that:
m0 = Monster(name='X')
m1 = Monster(name='godzilla', behaviour='godzilla behaviour')
Another way of doing what you want could be to dynamically create monster classes. e.g.
def make_monster_class(traits):
class DynamicMonster(object):
pass
for key, val in traits.items():
setattr(DynamicMonster, key, property(lambda self, val=val: val))
return DynamicMonster()
blue_monster = make_monster_class({"height": 4.3, "color": "blue"})
red_monster = make_monster_class({"color": "red", "has_claws": True})
for check in ("height", "color", "has_claws"):
print "blue", check, getattr(blue_monster, check, "N/A")
print "red ", check, getattr(red_monster, check, "N/A")
Output:
blue height 4.3
red height N/A
blue color blue
red color red
blue has_claws N/A
red has_claws True
I don't necessarily recommend this (the __getattr__ solution is generally preferable) but you could write your class so that all instances made from it have their own class (well, a subclass of it). This is a quick hacky implementation of that idea:
class MyClass(object):
def __new__(Class):
Class = type(Class.__name__, (Class,), {})
Class.__new__ = object.__new__ # to prevent infinite recursion
return Class()
m1 = MyClass()
m2 = MyClass()
assert type(m1) is not type(m2)
Now you can set properties on type(self) with aplomb since each instance has its own class object.
#Claudiu's answer is the same kind of thing, just implemented with a function instead of integrated into the instance-making machinery.

Categories

Resources