Why does PyCharm raise a warning when using #property here? - python

In tutorials I have seen two types of instance attribute naming for the purpose of using #property. Here is code showing examples of both. They also seem to work differently.
class A:
def __init__(self, x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x > 1000:
self.__x = 1000
else:
self.__x = x # Instance attribute __x defined outside __init__
class B:
def __init__(self, x):
self._x = x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
if x > 1000:
self._x = 1000
else:
self._x = x
a = A(9999)
print(a.x) # -> 1000
b = B(9999) # -> 9999
print(b.x)
b.x = 9999
print(b.x) # -> 1000
I like the behaviour of class A better as it seems that the #x.setter is used immediately in __init__, however that piece of code gives me a warning in PyCharm (I have it as a comment). Why would there be a warning if that is the proper use of a Python's property setter? There are no warnings in class B. Could I somehow call #x.setter in __init__ the same way as in class A without a warning?

It seems to be a bug in PyCharm: https://youtrack.jetbrains.com/issue/PY-25263.
A temporary solution I found was to add self._x = None in the __init__. So the code would be:
class A:
def __init__(self, x):
self._x = None
self.x = x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
if x > 1000:
self._x = 1000
else:
self._x = x
a = A(9999)
print(a.x) # -> 1000

Related

Simple python inheritance with singleton class failing with no stack trace

class B:
#property
def x(self):
print('x getter')
return self.x
#x.setter
def x(self, x) -> None:
print('x setter')
self.x = x + 1
class A(B):
__instance = None
def __new__(self):
''' Virtually private constructor '''
if not A.__instance:
A.__instance = object.__new__(self)
A.__instance.__setup()
return A.__instance
def __setup(self):
self.x = 10
def minus(self):
self.x -= 3
a1 = A()
Class A is a singleton class.
I'm not sure what is causing the program to fail as there is no stack trace and it just fails.
You are not using #property and setter correctly. You need a different name for the actual underlying property:
class B:
#property
def x(self):
print('x getter')
return self._x
#x.setter
def x(self, x) -> None:
print('x setter')
self._x = x + 1

Overriding a descriptor with a regular attribute

Is it possible to override a property in a base class by a regular attribute in a derived class, something like this:
class A(object):
#property
def x(self):
return self._x
#x.setter
def x(self, y):
self._x = y
class B(A):
def __init__(self, y):
self.x = y #the descriptor methods are not called and
#"x" is a regular attribute in the object dict.
The reason I am asking is because I have a complex base class in which one of the descriptor attributes typically performs a complicated calculation. However, in one of the derived classes, the returned value is trivial and it seems like a waste to have to override with another descriptor and not just a regular storage attribute.
You can simply redeclare x in B:
class A(object):
#property
def x(self):
print("calculating x...")
return self._x
#x.setter
def x(self, y):
print('setting x...')
self._x = 10*y
class B(A):
x = None
def __init__(self, y):
self.x = y #the descriptor methods are not called and
#"x" is a regular attribute in the object dict.
b = B(3)
print(b.x)
# 3

How to create a setter without a getter in Python? [duplicate]

This question already has answers here:
Python class #property: use setter but evade getter?
(4 answers)
Closed 4 years ago.
class My_Class:
def __init__(self):
self._x = 0
#property
def x(self):
return self._x
#x.setter
def x(self, x):
self._x = x
If I delete the following getter from the code above:
#property
def x(self):
return self._x
The code stops working. How can I create a setter without a getter?
The property function does not have to be used as a decorator:decorator can be used as a function:
class My_Class:
def _set_x(self, value):
self._x = value
x = property(fset=_set_x) # now value has only a setter
del _set_x # optional: delete the unneeded setter function
instance = My_Class()
instance.x= 8 # the setter works
print(instance._x) # the "private" value
print(instance.x) # raises: AttributeError: unreadable attribute
class My_Class:
def __init__(self):
self._x = 0
#property
def x(self):
raise RuntimeError('This property has no getter!')
#x.setter
def x(self, x):
self._x = x
Here's an alternative answer to what I already offered: make your own write-only descriptor.
class WriteOnly:
def __init__(self, private_name):
self.private_name = private_name
def __set__(self, obj, value):
obj.__dict__[self.private_name] = value
def __get__(self, obj, type=None):
raise AttributeError('unreadable attribute')
class My_Class:
x = WriteOnly('_x')
instance = My_Class()
instance.x = 8 # the setter works
print(instance._x) # the "private" value
print(instance.x) # raises: AttributeError: unreadable attribute

Defining Python class instance attributes

Given a class definition that allows 3 possible inputs:
class FooBar(object):
def __init__(self, x=None, y=None, z=None):
if x is not None:
self.x = x
elif if y is not None:
self.y = y
elif if z is not None:
self.z = z
else:
raise ValueError('Please supply either x,y or z')
This 3 inputs are related each other, lets say:
x = .5*y = .25*z
This also implies:
y = .5*z = 2*x
and
z = 2*y = 4*x
When creating a instance of FooBar(), the user need to supply one of those and the __init__ takes care of it.
Now I would like to do the following
If any one of the 3 variables are changed the others change following the relationship.
To try to accomplish that I did:
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
self._y = 2*self._x
self._z = 4*self._x
And to the others:
#property
def y(self):
return self._y
#y.setter
def y(self, value):
self._y = value
self._x = .5*self._y
self._z = 2*self._y
#property
def z(self):
return self._z
#z.setter
def z(self, value):
self._z = value
self._x = .25*self._z
self._y = .5*self._z
Is this the correct approach?
I think you make this more complicated than you have to. If the variables are related, and one can fully be determined by the other, there is no need to store three variables. You can store one variable, and dynamically calculate the others. Like:
class Foo(object):
def __init__(self, x=None, y=None, z=None):
if x is not None:
self.x = x
elif x is not None:
self.y = y
elif z is not None:
self.z = z
else:
raise ValueError('Provide an x, y, or z value.')
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = x
#property
def y(self):
return self._x / 2.0
#y.setter
def y(self, value):
self._x = 2 * value
#property
def z(self):
return self._x / 4.0
#z.setter
def z(self, value):
self._x = 4 * value
We thus store only a _x attribute on the class, and all the rest of the getters and setters, use the _x attribute (we can of course use _y or _z instead).
Furthermore something that is not very elegant is that a programmer can instantiate a Foo(x=1, y=425). As you can see, that means that it contains inconsistency. Perhaps it is worth raising an error in that case.
You can ensure that you only have one parameter provided by adding the following check in the init module:
class Foo(object):
def __init__(self, x=None, y=None, z=None):
data = [i for i in [x, y, z] if i is not None]
if len(data) > 1:
raise ValueError('Multiple parameters provided.')
if x is not None:
self.x = x
elif x is not None:
self.y = y
elif z is not None:
self.z = z
else:
raise ValueError('Provide an x, y, or z value.')
# ...
Here we thus construct a list of all non-None values, if there is more than one, the programmer provided two or more values, and then it is better to raise an exception.

An elegant way of overloading Python class properties

Consider a base class:
class A(object):
def __init__(self, x):
self._x = x
def get_x(self):
#...
return self._x
def set_x(self, x):
#...
self._x = x
x = property(get_x, set_x)
and a derived class:
class B(A):
def set_x(self, x):
#...
self._x = x**2
x = property(A.get_x, set_x)
Is there an elegant way of overloading set_x() in class B, without re-declaring it and the property x? Thank you.
Add an extra layer of indirection (i.e. use a hook):
class A(object):
def __init__(self, x):
self._x = x
# Using a _get_hook is not strictly necessary for your problem...
def _get_hook(self):
return self._x
def get_x(self):
return self._get_hook()
# By delegating `set_x` behavior to `_set_hook`, you make it possible
# to override `_set_hook` behavior in subclass B.
def _set_hook(self, x):
self._x=x
def set_x(self, x):
self._set_hook(x)
x = property(get_x, set_x)
class B(A):
def _set_hook(self, x):
print('got here!')
self._x = x**2
b=B(5)
b.x=10
# got here!
print(b.x)
# 100
For modern versions of Python, you can also use the #property decorator:
class A(object):
#property
def x(self):
return self._get_hook()
#x.setter
def x(self, x):
self._set_hook(x)
Try this one:
class A(object):
def __init__(self):
self._x = 0
def get_x(self):
#...
return self._x
def set_x(self, x):
#...
self._x = x
x = property(get_x, lambda self,x : self.set_x(x))
class B(A):
def set_x(self, x):
#...
self._x = x**2
The extra indirection given by the lambda will make the set_x function virtually.

Categories