Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent other classes' methods from calling my constructor

How do I make a python "constructor" "private", so that the objects of its class can only be created by calling static methods? I know there are no C++/Java like private methods in Python, but I'm looking for another way to prevent others from calling my constructor (or other method).

I have something like:

class Response(object):
    @staticmethod
    def from_xml(source):
        ret = Response()
        # parse xml into ret            
        return ret

    @staticmethod
    def from_json(source):
        # parse json
        pass

and would like the following behavior:

r = Response() # should fail
r = Response.from_json(source) # should be allowed

The reason for using static methods is that I always forget what arguments my constructors take - say JSON or an already parsed object. Even then, I sometimes forget about the static methods and call the constructor directly (not to mention other people using my code). Documenting this contract won't help with my forgetfulness. I'd rather enforce it with an assertion.

And contrary to some of the commenters, I don't think this is unpythonic - "explicit is better than implicit", and "there should be only one way to do it".

How can I get a gentle reminder when I'm doing it wrong? I'd prefer a solution where I don't have to change the static methods, just a decorator or a single line drop-in for the constructor would be great. A la:

class Response(object):
    def __init__(self):
        assert not called_from_outside()
like image 920
jdm Avatar asked Jul 30 '14 15:07

jdm


People also ask

Can constructor methods call other methods?

Yes, as mentioned we can call all the members of a class (methods, variables, and constructors) from instance methods or, constructors.

How do you not call a constructor in Java?

There is absolutely no way to do this in Java; it would break the language specification. Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure: Assign the arguments for the constructor [...]

Does child class always call parent constructor?

That depends on what you mean by "use." If you mean, does the default constructor for a child class call the parent constructor, then yes, it does (more below). If you mean, is a default constructor matching whatever parameters the parent constructor has created automatically, then no, not in the general case.

Can you call class methods in the constructor?

You can call other class methods from the constructor because the object is already initialized. The constructor also creates an object whose properties have their default values — either empty ( [] ) or the default value specified in the property definition block.


2 Answers

I think this is what you're looking for - but it's kind of unpythonic as far as I'm concerned.

class Foo(object):
    def __init__(self):
        raise NotImplementedError()

    def __new__(cls):
        bare_instance = object.__new__(cls)
        # you may want to have some common initialisation code here
        return bare_instance


    @classmethod
    def from_whatever(cls, arg):
        instance = cls.__new__(cls)
        instance.arg = arg
        return instance

Given your example (from_json and from_xml), I assume you're retrieving attribute values from either a json or xml source. In this case, the pythonic solution would be to have a normal initializer and call it from your alternate constructors, i.e.:

class Foo(object):
    def __init__(self, arg):
        self.arg = arg

    @classmethod
    def from_json(cls, source):
        arg = get_arg_value_from_json_source(source)
        return cls(arg)

    @classmethod
    def from_xml(cls, source):
        arg = get_arg_value_from_xml_source(source)
        return cls(arg)

Oh and yes, about the first example: it will prevent your class from being instantiated in the usual way (calling the class), but the client code will still be able to call on Foo.__new__(Foo), so it's really a waste of time. Also it will make unit testing harder if you cannot instantiate your class in the most ordinary way... and quite a few of us will hate you for this.

like image 162
bruno desthuilliers Avatar answered Oct 01 '22 20:10

bruno desthuilliers


I'd recommend turning the factory methods into module-level factory functions, then hiding the class itself from users of your module.

def one_constructor(source):
    return _Response(...)

def another_constructor(source):
    return _Response(...)

class _Response(object):
    ...

You can see this approach used in modules like re, where match objects are only constructed through functions like match and search, and the documentation doesn't actually name the match object type. (At least, the 3.4 documentation doesn't. The 2.7 documentation incorrectly refers to re.MatchObject, which doesn't exist.) The match object type also resists direct construction:

>>> type(re.match('',''))()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create '_sre.SRE_Match' instances

but unfortunately, the way it does so relies upon the C API, so it's not available to ordinary Python code.

like image 43
user2357112 supports Monica Avatar answered Oct 01 '22 20:10

user2357112 supports Monica