Using error object as function to control values of objects

A forum for general discussion of the Python programming language.

Using error object as function to control values of objects

Postby Bulgan » Wed Dec 25, 2013 3:48 pm

Hello everybody.
I have been learning python from no long time ago. But nearly at the beginning I stumbled on simple question:
how to set a restriction (limitation) on object value or object's properties without making a difficult class description. There is no problem with python's unstrict typification, not only with that. I search for the methods to control not only the variable types, but the variable value diapasons also. For example let creating the classical educational class - "Circle". A Circle - object should have three properties: two origin coordinates (some x, y) and a radius, in that (in our human visible world) x and y should be real numerical, a radius should be real positive numerical.
I somehow solved this task, You may see it there. https://docs.zoho.com/file/1m46652a001f1735b4ad8847bfb5a56a20ffa Nevertheless I utterly displeased with way I have done that. The code is huge for so small conception, and it's almost no reusable (I know about the descriptors - it will give the near same result). I would prefer some declarative stile. Mark Summerfield the author of book "Programming in Python 3: A Complete Introduction to the Python Language" (where is this "circle" task from, and I will recommend this book for everybody) in his book suggest using Assert - operator (290 page in russian translated version). I don't think implementing the debugging operator in a interface part of a program is good idea. "Assert" is great tool for save the time when you test the inner behavior of program but for the outer... - no.
When I had been starting search for an alternative my first thought was to add my own operator. But after hard googling I realized that it impossible to make that in python itself (I found some construction with lambda - function and some with bottle framework, but it not that I need). Such as I only newbie, and entering in python developers team or forked python ( :)) is no way for me, I gave up for this thoughts.
The another possibly path in declarative stile is something as that:
Code: Select all
radius=radius if radius>=0 else raise ValueError("Attribute 'radius' should be no negative")
radius=radius if isinstance(radius,(float,int)) else raise TypeError("Attribute should be numerical")

of course it not work, because we haven't ability use 'raise' by that. I should be able call errors object as function to brought it for reality. The simplest way to do that it wrote the error-raised function like this:
Code: Select all
def fValueError(message=""):
   raise ValueError(message)

or more universal:
Code: Select all
def SomeError(exception=Exception, message="Something don't well"):
   if isinstance(exception.args,tuple):
      raise exception
   else:
      raise exception(message)

So we can write:
Code: Select all
from ErrorMaker import SomeError
radius=radius if radius>=0 else SomeError(ValueError("Attribute 'radius' should be no negative"))

Or even like this:
Code: Select all
@dfValueError(['isinstance(radius,(float,int))'],"Attribute 'radius' should be no negative")
@dfTypeError(['radius>=0'],"Attribute should be numerical")
def fun_radius(x)
    return x

radius=fun_radius(radius)

The codes for decorators in last examples is not showed at there, but I think you have understood principles.
The small problem with this approach is in the more complex and less informatively evident error outing print. At the first the error message lead to the fake error raised function, the place where "oops" actually occurred only in second lines. It may be seemed some comically (that I wonder about it) but, when having been exploring in other's program such trifles could waste a lot of time.
So, I want to make ability call error objects as functions. To do that I was try to "breed" the ValueError class (as example) with adding __call__ method in heir. Actually, I don't know well how error's "raise" mechanism work to do that.
At today my laboratory pet - fValueError can be raised as such as any another but still not callable although has call - method.

Now. Before you send me away for haskell, or farther ;) let me ask you to help me save plenty of time by answer 3 questions:

1) Have you read, heard, tested, or developed something about premises problems? Ideas never come in only one head, maybe I "contrive bicycle"? Above all I'm interested in the questions of a variable value's diapasons restriction, but any helpful information would be welcomed and thanked (maybe very very thanked)
2) Would somebody help me (by any way) create callable error python class?
3) Would somebody look for use such (function-style "error creation") module, in another word: I should write only for myself or more accuracy. And if above is "no": What style of fake Error-function call you prefer to use?

P.S. I'm rarely write something on English (actually it only 2 or 3 article within my whole life), so my text be possible full of mistakes, I'm sorry about that. I will grateful for any criticism (in private) that help me became better.
Last edited by Mekire on Wed Dec 25, 2013 4:00 pm, edited 1 time in total.
Reason: First post lock.
Bulgan
 
Posts: 2
Joined: Sun Dec 22, 2013 8:56 pm

Re: Using error object as function to control values of obje

Postby micseydel » Fri Jan 10, 2014 12:34 am

I think the language barrier here added to my difficulty understanding your posts but I think I understood some things that you could use some feedback on. I should say upfront that I'm not sure what "diapasons" are and Google wasn't any help. Perhaps it's not an English word?

I'm posting the code you provided in the link here, so that other members don't have to go to a webpage, download a file, open the file, switch windows...
Code: Select all
#
import math
class Circle
(object):
#    __slots__=('x','y')
    def __init__(self, radius, x=0, y=0):
    #    self.__dict__={'x':x,'y':y}
        self.radius=radius
        self
.x=x
        self
.y=y
    def __setattr__
(self,name,value):
        
        def setting_atr
(name,value):
            if isinstance(value,(float,int)):
                if name=='radius' and value<0:
                    raise ValueError("Attribute 'radius' should be no negative")
                self.__dict__[name]=value
            else
:
                raise TypeError("Attribute should be numerical")
        #
        if len(self.__dict__)<3: #number of object attribute
            return setting_atr(name,value)
        elif name not in self.__dict__:
            raise AttributeError("'Circle' object has no such attribute")
        else:
            return setting_atr(name,value)
    @property
    def distance_from
(self):
        return math.hypot(self.x, self.y)-self.radius
    
@property
    def area
(self):
        return math.pi*self.radius**2
    def __eq__
(self, other):
        if not isinstance(other, Circle):
            return NotImplemented
        return self
.radius==other.radius
    def __repr__
(self):
        return "Circle({0.x!r},{0.y!r},{0.radius!r})".format(self)
    def __str__(self):
        return "({0.x!r},{0.y!r},{0.radius!r})".format(self)
    def __lt__(self,other):
        if isinstance(other, Point):
            return self.radius<other.radius
        return NotImplemented

You're right that this code is bloated and unpleasant. It's because you're doing something very discouraged in Python: type checking. In Python we do duck typing. Don't check if something is a circle. Just do the calculation. If the thing doesn't have a radius, then you'll still get failure. isinstance() should be used very sparingly, not all over the place to enforce something unnecessary (and some would say, overly inhibitory). If static typing really means that much to you, you shouldn't be using Python, since you get no compile-time checks for all that work anyway.

Regarding declarative style. That's not Python. Python supports many programming paradigms, even functional, but not declarative. Python is imperative. Again, if you want that style you should be using another language. I don't know Haskell that well, which you mention later in your post, but I have used Prolog in the past. I wouldn't try to program in Prolog as I do in Python, and I don't suggest the opposite either. There are also programs I really prefer to write in each language, since they have their own strong suites.

Strictly speaking, assert is not an operator. I don't mean to nit-pick your English, this is technical language that even experienced engineers will get wrong, and in cases like this, it matters if you know about different programming paradigms and want to understand Python. Python operators are always part of an expression. They're part of something that has a value. In Python, assert as well as raise, return, yield and in Python 2 print are all statements. Statements don't resolve to a value, they change the program's state in some way. They have side effects. I believe Ruby and Lisp, as well as perhaps Scala, have everything being an expression (I could be wrong about one or two of those exact languages). This just isn't how Python is.

Bulgan wrote:I don't think implementing the debugging operator in a interface part of a program is good idea. "Assert" is great tool for save the time when you test the inner behavior of program but for the outer... - no.

I don't understand what you mean when you say this. Asserts are for debugging, and can be optimized out of compiled bytecode, they're just for catching bugs when you really expect something else. In relation to an "interface", asserts aren't going to give compile-time help to a user or help with control flow, they're there to make a program fail early when something would go wrong later anyway. Again though, you shouldn't be type checking anyway. And if you're really concerned about negative integers, I wouldn't use an assert which could get optimized out, I'd use a custom exception and perhaps catching it higher up. Assert just says "DIE NOW!"

Your talk about new operators reminds me of Prolog here again. Python doesn't like that idea, we'd prefer well-named functions without side effects (using return values rather than modifying external state) instead of new syntax. It makes code much more uniform, assuming you follow the convention of no side-effects for your functions.

Bulgan wrote:The another possibly path in declarative stile is something as that:
Code: Select all
radius=radius if radius>=0 else raise ValueError("Attribute 'radius' should be no negative")
of course it not work, because we haven't ability use 'raise' by that. [...]

This is really unpythonic, including the version where you wrap the statement in a function. You're using it in an assignment, so it's extremely confusing and misleading that an exception can be raised, not to mention the fact that the only other logic branch is... nothing. Nothing changes. Plus you write more code than you need just to make it less standard. Just do this
Code: Select all
if radius < 0:
    raise ValueError("Attribute 'radius' should be no negative ({})".format(radius))


Regarding decorators, I can appreciate the desire there but there's a cleaner way to do it, if you were to do the type checking (which I recommend against).
Code: Select all
@accepts((float, int))
@returns(float)
def fun_radius(x):
    return float(x)

You could create your own custom PositiveInteger class too, which although you wouldn't use isinstance() in exactly the same way would be clear and all. You could also have an accepts decorator take a callable, which defines classes of types more strictly, is named well, reusable, and separates the logic. (I recommend against using a lambda in the case of accepts() taking a callable.) But again, there's no compile time analysis here and we prefer duck typing.

Bulgan wrote:1) Have you read, heard, tested, or developed something about premises problems? Ideas never come in only one head, maybe I "contrive bicycle"? Above all I'm interested in the questions of a variable value's diapasons restriction, but any helpful information would be welcomed and thanked (maybe very very thanked)

I Googled "premises problems" and didn't come up with anything. Lost in translation perhaps? No idea about the "contrive bicycle" either. Wish I could answer your question better.
Bulgan wrote:2) Would somebody help me (by any way) create callable error python class?

I'm not sure what you mean. Do you mean just inheriting from Exception and writing a __call__ method?
Bulgan wrote:3) Would somebody look for use such (function-style "error creation") module, in another word: I should write only for myself or more accuracy. And if above is "no": What style of fake Error-function call you prefer to use?

I don't believe something like that exists because that's not how Python is done. Just use raise when needed.
Join the #python-forum IRC channel on irc.freenode.net!

Please do not PM members regarding questions which are meant to be discussed publicly. The point of the forum is so that others can benefit from it. We don't want to help you over PMs or emails.
User avatar
micseydel
 
Posts: 1367
Joined: Tue Feb 12, 2013 2:18 am
Location: Mountain View, CA


Return to General Discussions

Who is online

Users browsing this forum: Bing [Bot] and 2 guests