Cannot access member variable using abc module and properties in python
-
19-06-2021 - |
Frage
I wrote a code that simulates the use of abc
module and properties
. However, it seems that I couldn't be able to access width
and height
variables. The code is as the following:
from abc import ABCMeta, abstractmethod
class Polygon:
__metaclass__ = ABCMeta
@abstractmethod
def compute_area(self): pass
def __init__(self):
self.width = None
self.height = None
@property
def width_prop(self):
return self.width
@property
def height_prop(self):
return self.height
@width_setter.setter
def width_setter(self, width):
self.width = width
@height_setter.setter
def height_setter(self, height):
self.height = height
class Triangle(Polygon):
def compute_area(self):
return 0.5 * width * height
if __name__ == "__main__":
tri = Triangle()
tri.height_setter(20)
tri.width_setter(30)
print "Area of the triangle = ", tri.compute_area()
The error message that I obtained is NameError: name 'width_setter' is not defined
. What could be wrong in my implementation?
EDIT:
from abc import ABCMeta, abstractmethod
class Polygon:
__metaclass__ = ABCMeta
@abstractmethod
def compute_area(self): pass
def __init__(self):
self.width = None
self.height = None
@property
def width_prop(self):
return self.width
@width_prop.setter
def width_setter(self, width):
self.width = width
@property
def height_prop(self):
return self.height
@height_prop.setter
def height_setter(self, height):
self.height = height
class Triangle(Polygon):
def compute_area(self):
return 0.5 * self.width * self.height
if __name__ == "__main__":
tri = Triangle()
tri.height_prop = 20
tri.width_prop = 30
print "Area of the triangle = ", tri.compute_area()
Lösung
edit: In such simple case like example use direct variable access. See Ned Batchelder's answer.
If you need extra functionality when accessing variables you can use this.
http://docs.python.org/library/functions.html#property
from abc import ABCMeta, abstractmethod
class Polygon(object):
__metaclass__ = ABCMeta
@abstractmethod
def compute_area(self):
pass
def __init__(self):
self._width = None
self._height = None
@property
def width(self):
getting_variable_value()
return self._width
@width.setter
def width(self, width):
setting_variable_value()
self._width = width
@property
def height(self):
getting_variable_value()
return self._height
@height.setter
def height(self, height):
setting_variable_value()
self._height = height
class Triangle(Polygon):
def compute_area(self):
return 0.5 * self.width * self.height
if __name__ == "__main__":
tri = Triangle()
tri.height = 20
tri.width = 30
print "Area of the triangle = ", tri.compute_area()
Andere Tipps
Write Python as Python, not as C++ or Java:
class Polygon:
def compute_area(self): # if you need this at all...
raise NotImplementedError() # what does it do for you?
def __init__(self):
self.width = None
self.height = None
class Triangle(Polygon):
def compute_area(self):
return 0.5 * self.width * self.height
if __name__ == "__main__":
tri = Triangle()
tri.height = 20
tri.width = 30
print "Area of the triangle = ", tri.compute_area()
"fixed" (but totally unpythonic) code:
class Polygon(object):
__metaclass__ = ABCMeta
@abstractmethod
def compute_area(self):
pass
def __init__(self):
self.width = None
self.height = None
@property
def width_prop(self):
return self.width
@width_prop.setter
def width_prop(self, width):
self.width = width
@property
def height_prop(self):
return self.height
@height_prop.setter
def height_prop(self, height):
self.height = height
class Triangle(Polygon):
def compute_area(self):
return 0.5 * self.width_prop * self.height_prop
if __name__ == "__main__":
tri = Triangle()
tri.height_prop = 20
tri.width_prop = 30
print "Area of the triangle = ", tri.compute_area()
Now for the serious part: Python is not Java. You don't need getters/setters for plain attributes access, since Python as a pretty good support for computed attributes (the property
type you failed to use correctly being a very simplistic generic - but very handy - implementation of). Your getters and setters are not doing anything useful, and you'll have the very same result (with less code and better perfs) accessing the attributes directly:
class Polygon(whatever):
__metaclass__ = ABCMeta
def __init__(self, witdh=None, height=None):
self.width = width
self.height = height
@abstractmethod
def compute_area(self):
pass
class Triangle(Polygon):
def compute_area(self):
return 0.5 * self.width * self.height
if __name__ == "__main__":
tri = Triangle(20, 30)
print "Area of the triangle = ", tri.compute_area()
For the record, Python doesn't have "private" / "public" keywords - only a convention that names starting with a leading underscore are "implementation stuff" and shouldn't be accessed by client code. Note that the "shouldn't" only means what it means : nothing prevents you from accessing them, but then don't complain if anything breaks, now or in next release. Kind of "warranty void if unsealed".
wrt/ proper use or property: I won't go into details (would require an in-depth Python execution model and object model explanation), but the correct syntax is:
class MyClass(object):
def __init__(self, prop):
# this will invoke prop.setter
self.prop = prop
# defines a property named "prop"
# will be accessed just like a plain
# attribute but will go thru the getter and setter
@property
def prop(self):
# this is the getter
return self._some_val * 42
# now add a setter to 'prop':
@prop.setter
def prop(self, val):
self._some_val = val / 42
obj = MyClass(10)
print obj.prop
obj.prop = 5
print obj.prop
Also (and finally): Python has no "implicit this" (or "implicit self"). You must use self.attr
within a method to access any attribute.