How to create class from which I can use "get"? - python

In Python, I am trying to create a class that has attributes which I can "get" (sorry if this wording is not exactly correct).
Basically I am trying to define some class p which has attributes var1 and var2. So then I can use p.get("var1") and p.get("var2") to get the values of these respective attributes. How can I define something like this?

You can define a class with get() method and check if the instance has the attribute with built-in getattr() method as following:
class MyClass:
def get(self, property, default=None):
return getattr(self, property, default)
var1 = 'var1'
var2 = 'var2'
myInstance = MyClass()
print(myInstance.get('var1'))
print(myInstance.get('var3', 'NonExisting Attribute'))
Here's a working repl.it project that I just created: https://repl.it/#HarunYlmaz/OvalLiveMethod
You can also check if the instance has the attribute with hasattr() method:
class MyClass:
def get(self, property, default=None):
if hasattr(self, property):
return getattr(self, property)
else:
return default
# Or you can raise an exception here

For instance object
class Test:
def __init__(self):
self.a = 1
self.b = 2
def get(self, var):
return eval('self.%s' % var)
t = Test()
a = t.get('a')
print(a) ## output: 1
For class object
class Test:
a = 1
b = 2
#classmethod
def get(cls, var):
return eval('cls.%s' % var)
a = Test.get('a')
print(a) # output: 1

Related

How to pass value from class to class - Python

I am trying to pass a value from one function in a class to another function in a class. Below is some simplified code of what I'm trying to achieve.
class test:
def __init__(self):
self.differentvalue = 0
def set(self, value):
print(value)
self.differentvalue = value #this is not the same value as defined above - i.e. this is a new variable created in foo class i believe
class foo:
def __init__(self):
test.set(self, 5)
if __name__ == '__main__':
foo()
I do not want __init__ to be called so test().set(5) is not an option.
Cheers,
Sean
You have two options
Option #1, best option if you need to keep a different context for differtvalue for each instance of Test
class Test:
def __init__(self):
self.differentvalue = 0
def set(self, value):
self.differentvalue = value
class foo:
def __init__(self):
test = Test()
test.set(5)
Option #2, best if you need to keep the latest value for differentvalue across all Test classes
class Test:
__DIFFERENTVALUE = 0
def __init__(self):
pass
#staticmethod
def set(value):
Test.__DIFFERENTVALUE = value
class foo:
def __init__(self):
Test.set(5)
You could define a class variable with a value of None, then upon calling the setter for the first time, assign a value to it. Further calls to the setter will not change the value.
In the following example, an __init__ method is not required in Test.
class Test:
differentvalue = None
#classmethod
def set(cls, value):
if value is not None and Test.differentvalue is None:
Test.differentvalue = value
class foo:
def __init__(self):
Test.set(5)
if __name__ == '__main__':
foo()
print(Test.differentvalue)
Test.set(12)
print(Test.differentvalue)
output:
5
5 # the value did not change

Clean way to implement setter and getter for lots of properties?

I have known the use of setter and getter for several properties, how could I trigger a same function when any property changes?
For example, the following codes add a setter to property a.
class AAA(object):
def __init__(self):
...
#property
def a(self):
...
#a.setter
def a(self, value):
...
If the class has a lot of properties like a, b, ... , z, and I want to print something like property xxx is modified when any property changes.
It is stupid to add the similar getter and setter one by one.
I have read some related questions and answers, but I do not find the solution for many properties.
How to trigger function on value change?
Using #property versus getters and setters
Metaprogramming, using __setattr__ to intercept modification:
class AAA(object):
def __setattr__(self, attr, value):
print("set %s to %s" % (attr, value))
super().__setattr__(attr, value)
aaa = AAA()
aaa.x = 17
# => set x to 17
print(aaa.x)
# => 17
You can do similarly with __getattr__ for reading access.
You can use descriptors. Descriptors are, in layman's terms, reusable properties. The advantage over the __getattr__ and __setattr__ hooks is that you have more fine-grained control over what attributes are managed by descriptors.
class MyDescriptor:
def __init__(self, default='default'):
self.default = default
def __set_name__(self, owner, name): # new in Python3.6
self.name = name
def __get__(self, instance, owner):
print('getting {} on {}'.format(self.name, instance))
# your getter logic here
# dummy implementation:
if instance is not None:
try:
return vars(instance)[self.name]
except KeyError:
return self.default
return self
def __set__(self, instance, value):
print('setting {} on {}'.format(self.name, instance))
# your getter logic here
# dummy implementation:
vars(instance)[self.name] = value
class MyClass:
a = MyDescriptor()
b = MyDescriptor()
_id = 1
# some logic for demo __repr__
def __init__(self):
self.c = 'non-descriptor-handled'
self.id = MyClass._id
MyClass._id += 1
def __repr__(self):
return 'MyClass #{}'.format(self.id)
Demo:
>>> m1 = MyClass()
>>> m2 = MyClass()
>>> m1.c
'non-descriptor-handled'
>>> m1.a
getting a on MyClass #1
'default'
>>> m1.b
getting b on MyClass #1
'default'
>>> m1.b = 15
setting b on MyClass #1
>>> m1.b
getting b on MyClass #1
15
>>> m2.b
getting b on MyClass #2
'default'
One year after asking this question, I find a more elgant way to add getter and setter to multiple similar properties.
Just make a more 'abstract' function which returns decorated property. And pass each of these properties to this function with a for loop. Then the getter and setter of all these properties are added.
def propABC(arg):
# arg: 'a', 'b', 'c'
#property
def prop(self):
_arg = '_' + arg
return getattr(self, _arg)
#prop.setter
def prop(self, val):
_arg = '_' + arg
setattr(self, _arg, val)
print(f"Set prop {_arg}")
return prop
for key in ['a', 'b', 'c']:
exec(f"{key} = propABC('{key}')")

Change Python Class attribute dynamically

I have a Class B inheriting Class A with a class attribute cls_attr.
And I would like to set dynamically cls_attr in class B.
Something like that:
class A():
cls_attr= 'value'
class B(A):
def get_cls_val(self):
if xxx:
return cls_attr = 'this_value'
return cls_attr = 'that_value'
cls_attr = get_cls_val()
I tried several things. I know i might not be looking in the right place but i am out of solutions.
EDIT: Classes are django admin classes
Thanks.
class attributes can be read on the class or an instance, but you can only set them on the class (trying to set them on an instance will only create an instance attribute that will shadow the class attribute).
If the condition is known at import time, you can just test it in the class body:
xxx = True
class A(object):
cls_attr = 'value'
class B(A):
if xxx:
cls_attr = 'this_value'
else
cls_attr = 'that_value'
Now if you want to change it during the program's execution, you either have to use a classmethod:
class B(A):
#classmethod
def set_cls_attr(cls, xxx):
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
or if you need to access your instance during the test:
class B(A):
def set_cls_attr(self, xxx):
cls = type(self)
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
What about using classmethod and polymorphically overriding it in subclass?
class A:
#classmethod
def cls_attr(cls):
return 'value'
class B(A):
#classmethod
def cls_attr(cls):
if cond():
return 'this'
else:
return 'that'
assert A.cls_attr() == 'value'
cond = lambda: True
assert B.cls_attr() == 'this'
cond = lambda: False
assert B.cls_attr() == 'that'
The easiest solution for me is with property decorator:
class B:
#property
def attr_name(self):
""" do your stuff to define attr_name dynamically """
return attr_name
This seems to do what you want:
>>> class B(A):
#classmethod
def set_cls_val(cls, x):
if x == 1:
cls.cls_attr = "new"
>>> c = B()
>>> c.cls_attr
'value'
>>> c.set_cls_val(B, 1)
>>> c.cls_attr
'new'
>>> B.cls_attr
'new'
Just set it within the function.
EDIT: Updated to set the class attribute and not the instance attribute, thanks #bruno-desthuilliers.
EDIT: Updates once again, thanks #bruno-desthuilliers. I should think my answers through more clearly. But what you want is answered below.

Catching changes to a mutable attribute in python

I am using properties to execute some code every time there is a change to an attribute, like this:
class SomeClass(object):
def __init__(self,attr):
self._attr = attr
#property
def attr(self):
return self._attr
#attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
And this works great:
>>> a = SomeClass(5)
>>> a.attr = 10
Do some code here every time attr changes
But if I store a mutable object in attr instead, attr can be modified directly, bypassing the setter and my change-detection code:
class Container(object):
def __init__(self,data):
self.data = data
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
>>>
Let's assume that attr is only ever going to be used to store an object of type Container. Is there an elegant way to modify SomeClass and/or Container to make SomeClass execute _on_change whenever the Container object stored in attr is modified? In other words, I want my output to be:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
Here is another solution. Some kind of proxy class. You dont need to modify any classes to monitor attributes changes in them, only wrap object in ChangeTrigger derived class with ovverriden _on_change function:
class ChangeTrigger(object):
def __getattr__(self, name):
obj = getattr(self.instance, name)
# KEY idea for catching contained class attributes changes:
# recursively create ChangeTrigger derived class and wrap
# object in it if getting attribute is class instance/object
if hasattr(obj, '__dict__'):
return self.__class__(obj)
else:
return obj
def __setattr__(self, name, value):
if getattr(self.instance, name) != value:
self._on_change(name, value)
setattr(self.instance, name, value)
def __init__(self, obj):
object.__setattr__(self, 'instance', obj)
def _on_change(self, name, value):
raise NotImplementedError('Subclasses must implement this method')
Example:
class MyTrigger(ChangeTrigger):
def _on_change(self, name, value):
print "New value for attr %s: %s" % (name, value)
class Container(object):
def __init__(self, data):
self.data = data
class SomeClass(object):
attr_class = 100
def __init__(self, attr):
self.attr = attr
self.attr_instance = 5
>>> a = SomeClass(5)
>>> a = MyTrigger(a)
>>>
>>> a.attr = 10
New value for attr attr: 10
>>>
>>> b = SomeClass(Container(5))
>>> b = MyTrigger(b)
>>>
>>> b.attr.data = 10
New value for attr data: 10
>>> b.attr_class = 100 # old value = new value
>>> b.attr_instance = 100
New value for attr attr_instance: 100
>>> b.attr.data = 10 # old value = new value
>>> b.attr.data = 100
New value for attr data: 100
Here is a version of SomeClass and Container that I think has the behavior you are looking for. The idea here being that modifications to Container will call the _on_change() function of the SomeClass instance that is associated with it:
class Container(object):
def __init__(self, data):
self.data = data
def __setattr__(self, name, value):
if not hasattr(self, name) or getattr(self, name) != value:
self.on_change()
super(Container, self).__setattr__(name, value)
def on_change(self):
pass
class SomeClass(object):
def __init__(self, attr):
self._attr = attr
self._attr.on_change = self._on_change
#property
def attr(self):
return self._attr
#attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
Example:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
>>> b.attr.data = 10 # on_change() not called if the value isn't changing
>>> b.attr.data2 = 'foo' # new properties being add result in an on_change() call
Do some code here every time attr changes
Note that the only change to SomeClass was the second line in __init__(), I just included the full code for completeness and easy testing.
If you want to track changes and don't want to mess with juggling with on_change() methods in different classes you could use functools.partial in the way shown starting here.
This way you can wrap your data and hide it totally. Get/change will be possible only via some methods melded inside that object.
NB: python has no private properties and on convention that we all are grownups and act against rules. In your case users of your api shouldn't change data on container (after creation) directly.
NB: here for those who may be interested in other ways...

In Python, count the number of variables in a class or prevent adding new class variables

In python, is there a way to prevent adding new class variables after defining the object?
For example:
class foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
bar = foo()
try:
bar.d = 4
except Exception, e:
print "I want this to always print"
Alternatively, is there a way to count the number of variables in an object?
class foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
def count(self):
...
bar = foo()
if bar.count() == 3:
print "I want this to always print"
The only way I thought of doing this was using a dictionary or list:
class foo:
def __int__(self):
self.dict = {'foo':1, 'bar':2}
self.len = 2
def chk():
return self.len == len(self.list)
However, doing this feels rather cumbersome for python. (obj.dict['foo']). I'd prefer just obj.foo if possible.
I want to have this so that I never accidentally declare a variable when I mean to change an existing one.
f = foo()
f.somename = 3
...
f.simename = 4 #this is a typo
if f.somename == 3:
solve_everything()
I suggest using __setattr__ to avoid the oddities of __slots__.
You always have to be careful when messing with __setattr__, since it takes care of setting all instance attributes, including those you set in __init__. Therefore it has to have some way of knowing when to allow the setting of an attribute, and when to deny it. In this solution I've designated a special attribute that controls whether new attributes are allowed or not:
class A(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
self.freeze = True
def __setattr__(self, attr, value):
if getattr(self, "freeze", False) and not hasattr(self, attr):
raise AttributeError("You shall not set attributes!")
super(A, self).__setattr__(attr, value)
Testing:
a = A()
try:
a.d = 89
except AttributeError:
print "It works!"
else:
print "It doesn't work."
a.c = 42
print a.a
print a.c
a.freeze = False
a.d = 28
a.freeze = True
print a.d
Result:
It works!
1
42
28
Also see gnibblers answer that wraps this concept neatly up in a class decorator, so it doesn't clutter up the class definition and can be reused in several classes without duplicating code.
EDIT:
Coming back to this answer a year later, I realize a context manager might solve this problem even better. Here's a modified version of gnibbler's class decorator:
from contextlib import contextmanager
#contextmanager
def declare_attributes(self):
self._allow_declarations = True
try:
yield
finally:
self._allow_declarations = False
def restrict_attributes(cls):
cls.declare_attributes = declare_attributes
def _setattr(self, attr, value):
disallow_declarations = not getattr(self, "_allow_declarations", False)
if disallow_declarations and attr != "_allow_declarations":
if not hasattr(self, attr):
raise AttributeError("You shall not set attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
And here's how to use it:
#restrict_attributes
class A(object):
def __init__(self):
with self.declare_attributes():
self.a = 1
self.b = 2
self.c = 3
So whenever you want to set new attributes, just use the with statement as above. It can also be done from outside the instance:
a = A()
try:
a.d = 89
except AttributeError:
print "It works!"
else:
print "It doesn't work."
a.c = 42
print a.a
print a.c
with a.declare_attributes():
a.d = 28
print a.d
In python, is there a way to prevent adding new class variables after defining the object?
Yes. __slots__. But do carefully read the notes.
How about a class decorator based on lazyr's answer
def freeze(cls):
_init = cls.__init__
def init(self, *args, **kw):
_init(self, *args, **kw)
self.freeze = True
cls.__init__ = init
def _setattr(self, attr, value):
if getattr(self, "freeze", None) and (attr=="freeze" or not hasattr(self, attr)):
raise AttributeError("You shall not set attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
#freeze
class foo(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
bar = foo()
try:
bar.d = 4
except Exception, e:
print "I want this to always print"
Preventing adding new attibutes using __slots__ class attribute:
class foo(object):
__slots__ = ['a', 'b', 'c']
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
bar = foo()
try:
bar.d = 4
except Exception as e:
print(e,"I want this to always print")
Counting attributes:
print(len([attr for attr in dir(bar) if attr[0] != '_' ]))
use this to count no.of attributes of an instance:
>>> class foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
>>> bar=foo()
>>> bar.__dict__
{'a': 1, 'c': 3, 'b': 2}
>>> len(bar.__dict__) #returns no. of attributes of bar
3
Do you mean new class variables or new instance variables? The latter looks like what you mean and is much easier to do.
Per Ignacio Vazquez-Abrams's answer, __slots__ is probably what you want. Just do __slots__ = ('a', 'b', 'c') inside of your class and that will prevent any other attributes from being created. Note that this only applies to instances of your class -- class-level attributes can still be set, and subclasses can add whatever attributes they please. And he is right -- there are some oddities, so read the linked documentation before you start sprinkling slots everywhere.
If you aren't using slots, return len(vars(self)) works as a body for your suggested count method.
As an alternative to slots, you could define a __setattr__ that rejects any attribute not on a "known good" list, or to reject any new attributes after a frozen attribute is set to True at the end of __init__, etc. This is harder to get right, but more flexible.
If you actually want your instances to be completely read-only after initialization, and you are using a recent version of Python, consider defining a namedtuple or subclass thereof. Tuple subclasses also have some limitations though; if you need to go this route I can expand on it, but I'd stick with slots unless you have a reason to do otherwise.
Suppose you now want your class to have a fixed set of both mutable and immutable attributes? I've hacked gnibbler's answer to make class attributes immutable after init:
def frozenclass(cls):
""" Modify a class to permit no new attributes after instantiation.
Class attributes are immutable after init.
The passed class must have a superclass (e.g., inherit from 'object').
"""
_init = cls.__init__
def init(self, *args, **kw):
_init(self, *args, **kw)
self.freeze = True
cls.__init__ = init
def _setattr(self, attr, value):
if getattr(self, "freeze", None):
if attr=="freeze" or not hasattr(self, attr):
raise AttributeError("You shall not create attributes!")
if hasattr(type(self), attr):
raise AttributeError("You shall not modify immutable attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
And an example:
#frozenclass
class myClass(object):
""" A demo class."""
# The following are immutable after init:
a = None
b = None
c = None
def __init__(self, a, b, c, d=None, e=None, f=None):
# Set the immutable attributes (just this once, only during init)
self.a = a
self.b = b
self.c = c
# Create and set the mutable attributes (modifyable after init)
self.d = d
self.e = e
self.f = f

Categories