I have the following class:
class temp_con():
def __init__(self):
self.t = 0
#property
def t(self):
return self.t
#t.setter
def t(self,value):
self.t = value
I need to use it to compare against a number following this logic:
if num <= temp_con.t - 2:
#dothing
However i get the error:
Type error: unsupported operand type for -: 'property' and 'int'<
I have tried int(temp_con.t) and getattr(temp_con, t) but those did not work.
How can I utilize the property as an int?
You need to use separate names for the property and the attribute it wraps. A good convention is to use the property name prefixed with a _ as the attribute name.
class TempCon:
def __init__(self):
self._t = 0
#property
def t(self):
return self._t
#t.setter
def t(self, value):
self._t = value
Then you can access the property on an instance of the class.
temp_con = TempCon()
print(temp_con.t)
temp_con.t = 5
print(temp_con.t)
You're accessing t on CLASS, not on an OBJECT of CLASS.
Try:
q = temp_con()
if num <= q.t - 2:
pass
In you code temp_con.t returns property object, which wraps getter (and setter) you've defined in your class code, but it doesnt execute it.
UPDATE: (memo: read twice)
There's also another problem with your code. First (well, it's second in code, but it will happen first) you define getter t, then later you OVERWRITE it with self.t = 0. As a result you'll get (as t) property accessible as a class member (which happens in your example) and value 0 as object's member.
You need an instance of the class in order to use the property and, as pointed out in other answers, you need to use a different name for your object variable. Try:
class temp_con():
def __init__(self):
self._t = 0
#property
def t(self):
return self._t
#t.setter
def t(self,value):
self._t = value
my_temp_con = temp_con()
if num <= my_temp_con.t - 2:
pass
Thus, to access the value of the property and not the property function, you have to access it through my_temp_con.t.
Related
Is it possible to create a class of integer where an instance of a certain class (say AutomaticCounter) will increase itself by some number (say 1) each time it is called?
>>> n = AutomaticCounter(start=0)
>>> print(n)
1
>>> print(n)
2
This is what I have tried so far:
class AutomaticCounter(int):
def __init__(self):
self = 0
def __str__(self):
self = self + 1
return self
If you really, really, really need to mangle an immutable and built-in type, then you can create a kind-of "pointer" to it:
class AutomaticCounter(int):
def __new__(cls, *args, **kwargs):
# create a new instance of int()
self = super().__new__(cls, *args, **kwargs)
# create a member "ptr" and storing a ref to the instance
self.ptr = self
# return the normal instance
return self
def __str__(self):
# first, create a copy via int()
# which "decays" from your subclass to an ordinary int()
# then stringify it to obtain the normal __str__() value
value = str(int(self.ptr))
# afterwards, store a new instance of your subclass
# that is incremented by 1
self.ptr = AutomaticCounter(self.ptr + 1)
return value
n = AutomaticCounter(0)
print(n) # 0
print(n) # 1
print(n) # 2
# to increment first and print later, use this __str__() instead:
def __str__(self):
self.ptr = AutomaticCounter(self.ptr + 1)
return str(int(self.ptr))
This, however, doesn't make the type immutable per se. If you do print(f"{self=}") at the beginning of __str__() you'll see the instance is unchanged, so you effectively have a size of 2x int() (+ some trash) for your object and you access the real instance via self.ptr.
It wouldn't work with self alone as self is merely a read-only reference (created via __new__()) passed to instance's methods as the first argument, so something like this:
def func(instance, ...):
instance = <something else>
and you doing the assignment would, as mentioned by Daniel, simply assign a new value to the local variable named instance (self is just a quasi-standard name for the reference) which doesn't really change the instance. Therefore the next solution which looks similar would be a pointer and as you'd like to manipulate it the way you described, I "hid" it to a custom member called ptr.
As pointed out by user2357112, there is a desynchronization caused by the instance being immutable, therefore if you choose the self.ptr hack, you'll need to update the magic methods (__*__()), for example this is updating the __add__(). Notice the int() calls, it converts it to int() to prevent recursions.
class AutomaticCounter(int):
def __new__(cls, *args, **kwargs):
self = super().__new__(cls, *args, **kwargs)
self.ptr = self
return self
def __str__(self):
value = int(self.ptr)
self.ptr = AutomaticCounter(int(self.ptr) + 1)
return str(value)
def __add__(self, other):
value = other
if hasattr(other, "ptr"):
value = int(other.ptr)
self.ptr = AutomaticCounter(int(self.ptr) + value)
return int(self.ptr)
def __rmul__(self, other):
# [1, 2, 3] * your_object
return other * int(self.ptr)
n = AutomaticCounter(0)
print(n) # 0
print(n) # 1
print(n) # 2
print(n+n) # 6
However, anything that attempts to pull the raw value or tries to access it with C API will most likely fail, namely reverse operations e.g. with immutable built-ins should be the case as for those you can't edit the magic methods reliably so it's corrected in all modules and all scopes.
Example:
# will work fine because it's your class
a <operator> b -> a.__operator__(b)
vs
# will break everything because it's using the raw value, not self.ptr hack
b <operator> a -> b.__operator__(a)
with exception of list.__mul__() for some reason. When I find the code line in CPython, I'll add it here.
Or, a more sane solution would be to create a custom and mutable object, create a member in it and manipulate that. Then return it, stringified, in __str__:
class AutomaticCounter(int):
def __init__(self, start=0):
self.item = start
def __str__(self):
self.item += 1
return str(self.item)
There are two issues here. First, self isn't actually the object but rather a variable reference to the object. When you reassign self, you're not changing the object but merely causing the self variable, which only has local scope, to now reference some other object. The original object remains unchanged.
Second, unless you really know what you're doing (and I don't), it is, in my opinion, unadvisable to subclass immutable built-ins. What you can do is have the object have an integer attribute and then define the __getattr__ method to pass any attribute calls on to the integer.
class AutomaticCounter:
def __init__(self, start=0):
self.item = start
def __str__(self):
self.item += 1
return str(self.item)
def __getattr__(self, attr):
return getattr(self.item, attr)
I'd like to create a class that has 2 input attributes and 1 output attribute such that whenever one of the input attributes are modified the output attribute is modified automatically
I've tried defining the attributes as instance variables within and outside the constructor function but in either case, after instantiating the object, the output attribute remains fixed at the value set at the moment of instantiation
class Example():
def __init__(self,n):
self.name=n
inA=1
inB=1
if inA==1 and inB==1:
outA=1
else:
outA=0
when instantiated outA is set to 1 as expected
but if I try to update:
object.inA=0
object.outA remains 1 whereas I need it to be updated to 0
Trying to avoid the use of functions if possible. New to python and OOP so sorry if this question is nonsensical or has an obvious answer
If you want instance attributes that depend on other instance attributes, properties are the way to go.
class Example:
def __init__(self, n):
self.name = n
self.inA = 1
self.inB = 1
#property
def outA(self):
return self.inA and self.inB
You access outA like a regular instance attribute, obj.outA.
>>> my_obj = Example("example")
>>> my_obj.outA
1
Changing the attributes inA and inB affect outA.
>>> my_obj.inA = 0
>>> my_obj.outA
0
You can create a function in the class and some other minor changes:
class Example():
def __init__(self,n):
self.name=n
self.inA=1
self.inB=1
def f(self):
if self.inA==1 and self.inB==1:
self.outA=1
else:
self.outA=0
To call it:
a = Example('foo')
a.inA = 0
a.f()
print(a.outA)
Output:
0
As you can see, taking out:
a.f()
line would make it give an error:
AttributeError: 'Example' object has no attribute 'outA'
Do you want it to return your output?
Expanding on U9-Forward's answer:
class Example():
def __init__(self,n):
self.name = n
self.inA = 1
self.inB = 1
def f(self):
return self.inA and self.inB
I am rather new to using classes. I struggle with functions (i.e. methods) in classes and how to access the classes attributes via method parameter.
My aim is to have a method accessing an instance's list (and the instances contained therein, yadda yadda)
While:
class dictclasser:
def __init__(self, attribute):
self.attribute = attribute
def printattr(self):
self.printattr2()
def printattr2(self):
return self.attribute
classcollection = []
while True:
attribute = input()
classcollection.append(dictclasser(attribute))
for i in classcollection:
print(i.printattr())
Returns None
class dictclasser:
def __init__(self, attribute):
self.attribute = attribute
def printattr(self):
return self.attribute
classcollection = []
while True:
attribute = input()
classcollection.append(dictclasser(attribute))
for i in classcollection:
print(i.printattr())
Returns everything as intended. I cannot figure out why printattr can access the instances attribute and printattr2 cannot. I have checked "Similar Question" to no avail.
Thanks in advance!
Because you missed a return statement in the first printattr. In order to propagate the return value of printattr2 onwards from printattr you have to return the returned value:
def printattr(self):
return self.printattr2()
Your printattr function has no return statement. Change
self.printattr2()
to
return self.printattr2()
Assume I were given the following class:
class foo(object):
def __init__(self, int):
self.count = int
def increment(self, int):
return foo(int + 1)
def decrement(self, int):
return foo(int - 1)
My goal is to chain together function calls to reach the result I want without having to assign each object to a variable. For instance, I know I can do this:
obj = foo(0)
obj = obj.increment(obj.count)
obj = obj.decrement(obj.count)
obj = obj.increment(obj.count)
obj = obj.decrement(obj.count)
print obj.count
0
but I would like to be able to do this:
finalcount = obj(0).increment(?.count).decrement(?.count)
but I don't know if there is something that I can put in place of ? to refer to the object who's method is being called since that object hasn't been assigned a name.
Your object doesn't really contribute anything in your current code. Note how the methods in your class don't refer to any object state — they never use self. Better would be to omit the int parameter entirely, and use self.count instead:
class foo(object):
def __init__(self, int):
self.count = int
def increment(self):
return foo(self.count + 1)
def decrement(self):
return foo(self.count - 1)
Then, what you wanted to write becomes this:
finalcount = foo(0).increment().decrement()
But, to answer your original question, there is no way to refer to the "current" object in the chain of calls. If you want to refer to an intermediate object, you must assign it to a variable.
Suppose a Python class has different methods, and depending on what the user specifies, a different method is carried out in the main function calculate().
In the example below the user needs to specify the keyword argument 'methodOne' or 'methodTwo'. If no or an incorrect keyword is specified it should default to 'methodOne'.
class someClass(object):
def __init__(self,method=None):
methodList = ['methodOne','methodTwo']
if method in methodList:
self.chosenMethod = method
else:
self.chosenMethod = self.methodOne
def methodOne(self):
return 1
def methodTwo(self):
return 2
def calculate(self):
return self.chosenMethod()
The above clearly does not work since method is a string and not a function. How can I select self.methedOne() or self.methedOne() based on my keyword argument method? In principle the following works:
def __init__(self,method=None):
if method == 'methodOne':
self.chosenMethod = self.methodOne
elif method == 'methodTwo':
self.chosenMethod = self.methodTwo
else:
self.chosenMethod = self.methodOne
But if I have more than two methods this becomes rather ugly. Is there a way to do this similar to my original code?
You could use getattr() for that purpose:
class someClass(object):
def __init__(self,method=None):
methodList = ['methodOne','methodTwo']
if method in methodList:
self.chosenMethod = method
else:
self.chosenMethod = self.methodOne
def methodOne(self):
return 1
def methodTwo(self):
return 2
def calculate(self):
return getattr(self, self.chosenMethod)()
x = someClass(method='methodOne')
print x.calculate()
>>> 1
You can use getattr to get the actual method on the class object.
class someClass(object):
def __init__(self,method=None):
# store it with the object so we can access it later in calculate method
self.method = method
def methodOne(self):
return 1
def methodTwo(self):
return 2
def calculate(self):
# get the actual method from the string here
# if no such method exists then use methodOne instead
return getattr(self, self.method, self.methodOne)()
> someClass('methodOne').calculate()
# 1
> someClass('methodTwo').calculate()
# 2