-1

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()
alfa_80
  • 427
  • 1
  • 5
  • 21
  • You must mean `@width_prop.setter` and `@height_prop.setter`. – stranac Jul 07 '12 at 21:11
  • Yes, I've done it. But still complaining.. – alfa_80 Jul 07 '12 at 21:22
  • Your setter functions need to be named the same as the properties. Also, please edit the question with the updated code. – stranac Jul 07 '12 at 21:28
  • Thanks for the reminder, I've just updated it. – alfa_80 Jul 07 '12 at 21:53
  • 3
    @Shah: don't burden your Python with all this noise. Omit all the getters and setters, just access the attributes directly. If you think you shouldn't, you need to learn more about Python. – Ned Batchelder Jul 07 '12 at 22:16
  • @Ned Batchelder: Thanks for your advice. But I read somewhere(http://stackoverflow.com/questions/6618002/python-property-versus-getters-and-setters) that suggests using properties for a good practise. Should the properties be ignored at all in python, I mean to follow a good practise? – alfa_80 Jul 07 '12 at 22:23
  • 1
    If your getters and setters are only what you show here, then you are wasting your time. You could simply access the attributes directly and get exactly the same effect. The question you linked to was about whether to write `get_my_attr` vs `@property def my_attr`. If you need to have code in one or the other, then sure, use a property. But if you have no code, and are merely forwarding on a privately-named attribute, then skip the whole thing and just use the attribute. – Ned Batchelder Jul 07 '12 at 22:50

3 Answers3

15

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()
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 1
    The reason he has a `compute_area` in his baseclass is because that way he can guarantee that all subclasses provide an implementation of it at definition time. – Voo Jul 08 '12 at 00:06
  • @Voo: except the base class doesn't have a useful definition, so what has he gained? – Ned Batchelder Jul 08 '12 at 00:11
  • 3
    That people who provide subclasses get the error at definition time and not only when someone sometime later calls `compute_area` (yes python is dynamic and all, but I personally always like getting errors sooner than later). Also it's a nice way to document in the code itself what methods a subclass has to provide instead of only in comment form. – Voo Jul 08 '12 at 00:14
  • @Voo: ok, we'll keep the base implementation of compute_area. – Ned Batchelder Jul 08 '12 at 00:18
  • 4
    @voo your point about documentation is valid but you are wrong about when you get the error. If a subclass doesn't implement the method, you'll only get an error when the method is used. The only difference is whether it's an AttributeError or NotImplementedError. – Tony Meyer Jul 08 '12 at 01:51
  • @Tony Oh I was sure that worked, thanks for the correction and sorry for spreading misinformation :) – Voo Jul 08 '12 at 01:59
  • @Tony, I haven't used any of this stuff, but the documentation for `abc` seems to say you can't instantiate a subclass that doesn't implement the method: http://docs.python.org/library/abc.html#abc.abstractmethod -- where is my misunderstanding? – Russell Borogove Jul 08 '12 at 05:05
  • What I compare the use of `abc` module to provide an abstract method with "exception handling-like method", I personally like to use `abc` one since it's an up-to-date version in python in providing a standardized interface to subclasses. – alfa_80 Jul 08 '12 at 07:09
  • @Ned Batchelder: Thanks anyway for the suggestion. – alfa_80 Jul 08 '12 at 07:09
  • @Tony Actually tested it and turns out I was somewhat right: `TypeError: Can't instantiate abstract class B with abstract methods foo` - I do get the error when instantiating an object (though *not* at definition time of the class). – Voo Jul 08 '12 at 11:19
  • @Voo that would only be the case if you are using abc, which Ned's code does not. – Tony Meyer Jul 12 '12 at 22:05
  • @RussellBorogove Ned's code doesn't use the abc module at all. – Tony Meyer Jul 12 '12 at 22:06
  • 1
    @Tony Well but the discussion was about the advantages of using the `abc` module after all. So one additional advantage is that we get the error earlier than otherwise (although not quite as early as I thought). – Voo Jul 12 '12 at 22:12
6

"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.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • I actually want to maintain the use abc as my above code. I still get this error: TypeError: 'NoneType' object is not callable – alfa_80 Jul 07 '12 at 21:04
  • Please read the edit. wrt/ your error: it comes with a full traceback, please read it, you should be able to fix the code by yourself (else start with the official Python tutorial - which you should obviously do anyway) – bruno desthuilliers Jul 07 '12 at 21:09
  • I don't think your "correct syntax" can work; using `whatever` instead of `prop` should generate an AttributeError. – DSM Jul 07 '12 at 21:13
  • I've tried also the edited version of yours, but it still complaints "tri.height_prop = 20 AttributeError: can't set attribute" – alfa_80 Jul 07 '12 at 21:19
  • @Shah : cf DSM comment. That's what I deserve for posting here late at night I suppose :/ – bruno desthuilliers Jul 07 '12 at 21:32
  • @bruno desthuilliers: Thanks anyway for the effort. – alfa_80 Jul 07 '12 at 21:54
  • I'm still getting an error message of " tri.height_prop = 20 AttributeError: can't set attribute" with the updated code. – alfa_80 Jul 07 '12 at 21:55
  • @Shah : I don't have this error. But anyway : this is *not* the proper way to write Python code. Don't use properties when you don't need them. *This is Python, not Java*. – bruno desthuilliers Jul 08 '12 at 08:33
  • @bruno desthuilliers: I guess, from your edited code, the line tri.height_prop = 20 should be written as tri.height = 20, then only it should work. – alfa_80 Jul 08 '12 at 08:36
-4

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()
reacher
  • 16
  • 3