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