Change class variable reference within the class - python

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.

Related

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.

Modifying class __dict__ when shadowed by a property

I am attempting to modify a value in a class __dict__ directly using something like X.__dict__['x'] += 1. It is impossible to do the modification like that because a class __dict__ is actually a mappingproxy object that does not allow direct modification of values. The reason for attempting direct modification or equivalent is that I am trying to hide the class attribute behind a property defined on the metaclass with the same name. Here is an example:
class Meta(type):
def __new__(cls, name, bases, attrs, **kwargs):
attrs['x'] = 0
return super().__new__(cls, name, bases, attrs)
#property
def x(cls):
return cls.__dict__['x']
class Class(metaclass=Meta):
def __init__(self):
self.id = __class__.x
__class__.__dict__['x'] += 1
This is example shows a scheme for creating an auto-incremented ID for each instance of Class. The line __class__.__dict__['x'] += 1 can not be replaced by setattr(__class__, 'x', __class__.x + 1) because x is a property with no setter in Meta. It would just change a TypeError from mappingproxy into an AttributeError from property.
I have tried messing with __prepare__, but that has no effect. The implementation in type already returns a mutable dict for the namespace. The immutable mappingproxy seems to get set in type.__new__, which I don't know how to avoid.
I have also attempted to rebind the entire __dict__ reference to a mutable version, but that failed as well: https://ideone.com/w3HqNf, implying that perhaps the mappingproxy is not created in type.__new__.
How can I modify a class dict value directly, even when shadowed by a metaclass property? While it may be effectively impossible, setattr is able to do it somehow, so I would expect that there is a solution.
My main requirement is to have a class attribute that appears to be read only and does not use additional names anywhere. I am not absolutely hung up on the idea of using a metaclass property with an eponymous class dict entry, but that is usually how I hide read only values in regular instances.
EDIT
I finally figured out where the class __dict__ becomes immutable. It is described in the last paragraph of the "Creating the Class Object" section of the Data Model reference:
When a new class is created by type.__new__, the object provided as the namespace parameter is copied to a new ordered mapping and the original object is discarded. The new copy is wrapped in a read-only proxy, which becomes the __dict__ attribute of the class object.
Probably the best way: just pick another name. Call the property x and the dict key '_x', so you can access it the normal way.
Alternative way: add another layer of indirection:
class Meta(type):
def __new__(cls, name, bases, attrs, **kwargs):
attrs['x'] = [0]
return super().__new__(cls, name, bases, attrs)
#property
def x(cls):
return cls.__dict__['x'][0]
class Class(metaclass=Meta):
def __init__(self):
self.id = __class__.x
__class__.__dict__['x'][0] += 1
That way you don't have to modify the actual entry in the class dict.
Super-hacky way that might outright segfault your Python: access the underlying dict through the gc module.
import gc
class Meta(type):
def __new__(cls, name, bases, attrs, **kwargs):
attrs['x'] = 0
return super().__new__(cls, name, bases, attrs)
#property
def x(cls):
return cls.__dict__['x']
class Class(metaclass=Meta):
def __init__(self):
self.id = __class__.x
gc.get_referents(__class__.__dict__)[0]['x'] += 1
This bypasses critical work type.__setattr__ does to maintain internal invariants, particularly in things like CPython's type attribute cache. It is a terrible idea, and I'm only mentioning it so I can put this warning here, because if someone else comes up with it, they might not know that messing with the underlying dict is legitimately dangerous.
It is very easy to end up with dangling references doing this, and I have segfaulted Python quite a few times experimenting with this. Here's one simple case that crashed on Ideone:
import gc
class Foo(object):
x = []
Foo().x
gc.get_referents(Foo.__dict__)[0]['x'] = []
print(Foo().x)
Output:
*** Error in `python3': double free or corruption (fasttop): 0x000055d69f59b110 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bcb)[0x2b32d5977bcb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76f96)[0x2b32d597df96]
/lib/x86_64-linux-gnu/libc.so.6(+0x7778e)[0x2b32d597e78e]
python3(+0x2011f5)[0x55d69f02d1f5]
python3(+0x6be7a)[0x55d69ee97e7a]
python3(PyCFunction_Call+0xd1)[0x55d69efec761]
python3(PyObject_Call+0x47)[0x55d69f035647]
... [it continues like that for a while]
And here's a case with wrong results and no noisy error message to alert you to the fact that something has gone wrong:
import gc
class Foo(object):
x = 'foo'
print(Foo().x)
gc.get_referents(Foo.__dict__)[0]['x'] = 'bar'
print(Foo().x)
Output:
foo
foo
I make absolutely no guarantees as to any safe way to use this, and even if things happen to work out on one Python version, they may not work on future versions. It can be fun to fiddle with, but it's not something to actually use. Seriously, don't do it. Do you want to explain to your boss that your website went down or your published data analysis will need to be retracted because you took this bad idea and used it?
This probably counts as an "additional name" you don't want, but I've implemented this using a dictionary in the metaclass where the keys are the classes. The __next__ method on the metaclass makes the class itself iterable, such that you can just do next() to get the next ID. The dunder method also keeps the method from being available through the instances. The dictionary storing the next id has a name starting with a double underscore, so it's not easily discoverable from any of the classes that use it. The incrementing ID functionality is thus entirely contained in the metaclass.
I tucked the assignment of the id into a __new__ method on a base class, so you don't have to worry about it in __init__. This also allows you to del Meta so all the machinery is a little harder to get to.
class Meta(type):
__ids = {}
#property
def id(cls):
return __class__.__ids.setdefault(cls, 0)
def __next__(cls):
id = __class__.__ids.setdefault(cls, 0)
__class__.__ids[cls] += 1
return id
class Base(metaclass=Meta):
def __new__(cls, *args, **kwargs):
self = object.__new__(cls)
self.id = next(cls)
return self
del Meta
class Class(Base):
pass
class Brass(Base):
pass
c0 = Class()
c1 = Class()
b0 = Brass()
b1 = Brass()
assert (b0.id, b1.id, c0.id, c1.id) == (0, 1, 0, 1)
assert (Class.id, Brass.id) == (2, 2)
assert not hasattr(Class, "__ids")
assert not hasattr(Brass, "__ids")
Note that I've used the same name for the attribute on both the class and the object. That way Class.id is the number of instances you've created, while c1.id is the ID of that specific instance.
My main requirement is to have a class attribute that appears to be read only and does not use additional names anywhere. I am not absolutely hung up on the idea of using a metaclass property with an eponymous class dict entry, but that is usually how I hide read only values in regular instances.
What you are asking for is a contradiction: If your example worked, then __class__.__dict__['x'] would be an "additional name" for the attribute. So clearly we need a more specific definition of "additional name." But to come up with that definition, we need to know what you are trying to accomplish (NB: The following goals are not mutually exclusive, so you may want to do all of these things):
You want to make the value completely untouchable, except within the Class.__init__() method (and the same method of any subclasses): This is unPythonic and quite impossible. If __init__() can modify the value, then so can anyone else. You might be able to accomplish something like this if the modifying code lives in Class.__new__(), which the metaclass dynamically creates in Meta.__new__(), but that's extremely ugly and hard to understand.
You want the code that manipulates the value to be "nicely encapsulated": Write a method in the metaclass that increments the private value (or does whatever other modification you need), and provide a read-only metaclass property that accesses it under the public name.
You are concerned about a subclass accidentally clashing names with the private name: Prefix the private name with a double underscore to invoke automatic name mangling. While this is usually seen as a bit unPythonic, it is appropriate for cases where name collisions may be less obvious to subclass authors, such as the internal names of a metaclass colliding with the internal names of a regular class instantiated from it.

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.

Best way to access a class variable in an object?

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.

How should I choose between using instance vs. class attributes?

I tried this example code:
class testclass:
classvar = 'its classvariable LITERAL'
def __init__(self,x,y):
self.z = x
self.classvar = 'its initvariable LITERAL'
self.test()
def test(self):
print('class var',testclass.classvar)
print('instance var',self.classvar)
if __name__ == '__main__':
x = testclass(2,3)
I need some clarification. In both cases, I'm able to access the class attribute and instance in the test method.
So, suppose if I have to define a literal that needs to be used across all function, which would be the better way to define it: an instance attribute or a class attribute?
I found this in an old presentation made by Guido van Rossum in 1999 ( http://legacy.python.org/doc/essays/ppt/acm-ws/sld001.htm ) and I think it explains the topic beautifully:
Instance variable rules
On use via instance (self.x), search order:
(1) instance, (2) class, (3) base classes
this also works for method lookup
On assigment via instance (self.x = ...):
always makes an instance variable
Class variables "default" for instance variables
But...!
mutable class variable: one copy shared by all
mutable instance variable: each instance its own
Class variables are quite good for "constants" used by all the instances (that's all methods are technically). You could use module globals, but using a class variable makes it more clearly associated with the class.
There are often uses for class variables that you actually change, too, but it's usually best to stay away from them for the same reason you stay away from having different parts of your program communicate by altering global variables.
Instance variables are for data that is actually part of the instance. They could be different for each particular instance, and they often change over the lifetime of a single particular instance. It's best to use instance variables for data that is conceptually part of an instance, even if in your program you happen to only have one instance, or you have a few instances that in practice always have the same value.
It's good practice to only use class attributes if they are going to remain fixed, and one great thing about them is that they can be accessed outside of an instance:
class MyClass():
var1 = 1
def __init__(self):
self.var2 = 2
MyClass.var1 # 1 (you can reference var1 without instantiating)
MyClass.var2 # AttributeError: class MyClass has no attribute 'var2'
If MyClass.var is defined, it should be the same in every instance of MyClass, otherwise you get the following behaviour which is considered confusing.
a = MyClass()
b = MyClass()
a.var1, a.var2 # (1,2)
a.var1, a.var2 = (3,4) # you can change these variables
a.var1, a.var2 # (3,4)
b.var1, b.var2 # (1,2) # but they don't change in b
MyClass.var1 # 1 nor in MyClass
You should define it as a class attribute if you want it to be shared among all instances. You should define it as an instance variable if you want a separate one for each instance (e.g., if different instances might have different values for the variable).

Categories

Resources