how to create nicer variable interface in python class - python

I want a variable to do more than just be set when I set it.
and the interface to be as clean as possible.
short: what I'd want:
# have class with a variable that I can access:
print myInstance.var
42
# change the variable
myInstance.var = 23
# have the change kick off another method:
self.var was changed: 23!!
hmm.. so what I can do: Use the variable and a setter method:
class Test:
def __init__(self):
self.var = 1
print( 'self.var is: ' + str(self.var) )
def setVar(self, value):
self.var = value
print( 'self.var changed: ' + str(self.var) )
t = Test()
self.var is: 1
# so I have t.var at hand:
print t.var
1
# and change it this way
t.setVar(5)
self.var changed: 5
But then i have 2 different things to work with..
Ok I could make a method to interact with the var:
class Test:
def __init__(self):
self.var = 1
print( 'self.var is: ' + str(self.var) )
def method(self, value=None):
if value == None:
return self.var
self.var = value
print( 'self.var changed: ' + str(self.var) )
t = Test()
self.var is: 1
# to get the value then:
print t.method()
1
# to set it:
t.method(4)
self.var changed: 4
# and verifiy:
print t.method()
4
This is nice already. I've seen it in different post on other languages. but I dunno. Is there a be better solution in python?!?
Maybe I'm paranoid but but to me it'd just feel nicer to just do t.var = 5 and have something kicked off too.

I think you want python Properties. Check this out. Something like:
class Test:
def __init__(self):
self._var = 1
#property
def var(self):
return self._var
#var.setter
def var(self, value):
# add stuff here that you want to happen on var assignment
self._var = value

You can use a property. Note that if the setter method is expensive, it's better to use a method. People expect attribute access to be fast.
class Foo(object):
def __init__(self):
self._var = None
#property
def var(self):
return self._var
#var.setter
def var(self, whatever):
self._var = whatever
do_whatever()
x = Foo()
print x.var # prints None
x.var = 2 # sets x.var and does whatever

Related

Property setter not working when attribute starts with "__"?

I am using Python 3.8.6 and this works fine
class A:
#property
def _a(self):
return getattr(self, '_a_', 0)
#_a.setter
def _a(self, value):
self._a_ = value
a = A()
print(a._a) # prints 0
a._a = 10
print(a._a) # prints 10 as expected
This doesn't work
class A:
#property
def _a(self):
return getattr(self, '__a', 0)
#_a.setter
def _a(self, value):
self.__a = value
a = A()
print(a._a) # prints 0
a._a = 10
print(a._a) # prints 0 again
That's mind blowing! the only difference between the first and second example is that the private attribute is __a instead of _a_
Any idea why? I wasn't able to figure it out
It's due to private name mangling, but it doesn't apply to the contents of string literals like the one you're passing to getattr().
Fortunately the fix is simple:
class A:
#property
def _a(self):
return getattr(self, '_A__a', 0)
#_a.setter
def _a(self, value):
self.__a = value
a = A()
print(a._a) # prints 0
a._a = 10
print(a._a) # prints 10 now

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

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

how to pass the name of a variable as a variable

How can I set the variable I want to change as a function argument? I want to define only one function, and not set_a(value), set_b(value), set_c(value), ...
class MyVarClass:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
# this works, but I don't want to write n functions
def set_a(myvar_object, value):
myvar_object.a = value
# this is what I actually want:
def set_vars(myvar_object, var_name, value):
myvar_object.var_name = value
myvar = MyVarClass()
# I want to do the same as myvar.a = 4
set_a(myvar, 4) # works as intended, now myvar.a is 4
set_vars(myvar, a, 4) # error, a is not defined
What you usually do is to create a method to the class like this:
class MyVarClass:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
def set_a(self, value):
self.a = value
If, for some reason, can't do it like this and you only have the name of the attribute as string, then you can use setattr:
setattr(myvar_object, 'a', value)
But usually what you do is just this line:
myvar_object.a = 4
This is done with setattr.
def set_vars(myvar_object, var_name, value):
setattr(myvar_object, var_name, value)
This isn't necessarily the best way of doing this. It often suggests a different data structure would be better, but in case that isn't the case here.
You'll note if you go this route, there isn't much reason to have a def rather than just call setattr directly, unless you think you'll change things in the future.
what about using a dict:
class MyVarClass:
def __init__(self):
self.vars = {"a": 1, "b": 2, "c": 3}
def set_vars(myvar_object, var_name, value):
myvar_object.vars[var_name] = value
**
var_name = ''
class MyVarClass:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
# this is what I actually want:
def set_vars(myvar, var_name, value):
myvar.var_name = value
print (myvar.var_name)
myvar = MyVarClass()
set_vars(myvar, "b", 6)
**

Accessing class methods inside another class at same scope

Trying to get a work around for getting access to a class method in another class while being inside a class. Code below will probably explain my goal.
class Access(object):
def __init__(self):
pass
def select(self, value):
Store.keep(value)
class Store(object):
def __init__(self):
self.store_value = 0
def keep(self, value):
self.store_value = value
x = Access()
y = Store()
x.select(10)
y.store_value
##Want the output of 10
I don't see any way to do what you want without Access having a reference to a Store object.
The closest thing you can do is
class Access(object):
def __init__(self):
pass
def select(self, value):
Store.keep(value)
class Store(object):
#classmethod
def keep(cls, value):
cls.store_value = value
x = Access()
y = Store()
x.select(10)
print y.store_value #will print 10
#but
z = Store()
print z.store_value #will print 10 too
Where store_value is shared by all instances of Store.
You could pass an instance to select also and use Store.keep(inst, value):
class Access(object):
def __init__(self):
pass
def select(self, inst, value):
Store.keep(inst, value)
class Store(object):
def __init__(self):
self.store_value = 0
def keep(self, value):
self.store_value = value
x = Access()
y = Store()
x.select(y, 10)
print(y.store_value)
10

Python Class Objects Attribute Referencing

I have two classes. a and b.
In one of class a's methods, I created an object of class b. One of class b attributes takes a function. So say I gave it a random function but does this function of class b have access to class a's attribute? even though I didn't pass it in directly as a parameter?
class b:
def __init__(self):
self.attribute_function = None
class a:
def __init__(self):
self.temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda self: self.counter < self.temp
return obj.attribute_function()
if __name__ == "__main__":
#pass
obj = a()
print obj.temp()
In the above example, I tried to provide a really basic example, but if you run it, it doesn't work...
Revised Code, class a should look like this:
class a:
def __init__(self):
self.temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda args: self.counter < self.temp
return obj.attribute_function(1) # i added this 1 to fill in arg
This works:
class b:
def __init__(self):
self.attribute_function = None
class a:
def __init__(self):
self._temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda self=self: self.counter < self._temp
return obj.attribute_function()
if __name__ == "__main__":
obj = a()
print obj.temp()
On problem you had is self.temp = 10 which shadowed your method temp().
Another problem: lambda self: self.counter < self._temp. Your lambda function was expecting an argument. But omitting self is not a good idea lambda : self.counter < self._temp, because if you call obj.attribute_function() somewhere where self is not available or has changed - it will not find self or use another self. self=self fixes that.
But generally such magic is an anti-pattern. Tell us what are your trying to achieve, and there should be a better way to do what you want. Otherwise this kind of code will ensure many headaches.
I think this is a better solution (called strategy pattern):
class B:
def __init__(self, a):
self.a = a
def temp(self):
return self.a.temp()
class A:
def __init__(self):
self._temp = 10
self.counter = 0
def temp(self):
return self.counter < self._temp
if __name__ == "__main__":
obj = B(A())
print obj.temp()
Your example does not work because you have a name collision at temp
You have assigned temp to be both a method:
def temp(self):
and an attribute:
self.temp = 10

Categories