7

I've been trying to figuring this out for the last few hours, and I'm about to give up.

How do you make sure that in python only a matching specific criteria will create the object?

For example, let's say I want to create an object Hand, and initialize a Hand only when I have enough Fingers in the initializer? (Please just take this as an analogy)

Say,

class Hand:
  def __init__(self, fingers):
    # make sure len(fingers)==5, and 
    #only thumb, index, middle, ring, pinky are allowed in fingers
    pass

Thanks.

These are the closest questions I found, but one is in C++, the other does not answer my question.

checking of constructor parameter

How to overload __init__ method based on argument type?

Community
  • 1
  • 1
chemelnucfin
  • 411
  • 1
  • 4
  • 9

2 Answers2

6

You have to define __new__ for that:

class Foo(object):
    def __new__(cls, arg):
        if arg > 10: #error!
            return None 
        return super(Foo, cls).__new__(cls)

print Foo(1)    # <__main__.Foo object at 0x10c903410>
print Foo(100)  # None

That said, using __init__ and raising an exception on invalid args is generally much better:

class Foo(object):
    def __init__(self, arg):
        if arg > 10: #error!
            raise ValueError("invalid argument!") 
        # do stuff
georg
  • 211,518
  • 52
  • 313
  • 390
  • 8
    Defining `__new__` is rarely needed, and returning `None` from it is horrible. Just throw an exception rather than going on for a couple of lines (or a dozen function calls, if you've got bad luck) before crashing with an inexplicable error. –  Jun 02 '12 at 23:44
  • @delnan - technically, if the OP is serious about "only a matching specific criteria will create the object," defining `__new__` would be needed, because by the time `__init__` is called an object has already been created (still, you're right that raising an exception in `__new__` would be better than returning None). But I'm guessing the OP doesn't really mean that, in which case the `__init__` version is definitely better. – weronika Jun 03 '12 at 05:55
  • 1
    @weronika Unless deep magic and awful hackery is involved, raising an exception in `__init__` prevents everyone from getting a reference to the not-really-constructed object. So from an observer's POV there's no difference beside one being a terrible API. –  Jun 03 '12 at 06:06
0

Try asserting:

class Hand(object):
    def __init__(self,fingers):
        assert len(fingers) == 5
        for fing in ("thumb","index","middle","ring","pinky"):
            assert fingers.has_key(fing)
Snakes and Coffee
  • 8,747
  • 4
  • 40
  • 60
  • 6
    No, `assert` is for verifying *internal* invariants: Checking that *your* code isn't horribly broken. `AssertionError` conveys no useful information to the caller, and may make her think your code is broken. Besides, assertions are stripped by `-O`. Throw a `ValueError` or `RuntimeError` or whatever is appropriate for the specific use case. –  Jun 02 '12 at 23:46