Mimic Python NoneType

I create several classes to use them as state flags (this is more an exercise, although I will use them in a real project), just like we use None in Python, i.e.

 ... some_var is None ... 

NoneType has several special properties, the most important is singleton, that is, there can be no more than one instance of NoneType during any interpreter session, and its instances ( None objects) are immutable. I came up with two possible ways to implement somewhat similar behavior in pure Python, and I really want to know which one looks better from an architectural point of view.

1. Do not use instances at all.

The idea is to have a metaclass that creates immutable classes. Classes are not allowed to have instances.

 class FlagMetaClass(type): def __setattr__(self, *args, **kwargs): raise TypeError("{} class is immutable".format(self)) def __delattr__(self, *args, **kwargs): self.__setattr__() def __repr__(self): return self.__name__ class BaseFlag(object): __metaclass__ = FlagMetaClass def __init__(self): raise TypeError("Can't create {} instances".format(type(self))) def __repr__(self): return str(type(self)) class SomeFlag(BaseFlag): pass 

And get the desired behavior

 a = BaseFlag a is BaseFlag # -> True a is SomeFlag # -> False 

Obviously, any attempt to set attributes for these classes will fail (of course, there are several hacks to overcome this, but the direct path is closed). And the classes themselves are unique objects loaded into the namespace.

2. The correct singleton class

 class FlagMetaClass(type): _instances = {} def __call__(cls): if cls not in cls._instances: cls._instances[cls] = super(FlagMetaClass, cls).__call__() return cls._instances[cls] # This may be slightly modified to # raise an error instead of returning # the same object, eg # def __call__(cls): # if cls in cls._instances: # raise TypeError("Can't have more than one {} instance".format(cls)) # cls._instances[cls] = super(FlagMetaClass, cls).__call__() # return cls._instances[cls] def __setattr__(self, *args, **kwargs): raise TypeError("{} class is immutable".format(self)) def __delattr__(self, *args, **kwargs): self.__setattr__() def __repr__(self): return self.__name__ class BaseFlag(object): __metaclass__ = FlagMetaClass __slots__ = [] def __repr__(self): return str(type(self)) class SomeFlag(BaseFlag): pass 

Here Flag classes are real singletones. This particular implementation does not cause an error when trying to create another instance, but returns the same object (although it is easy to change this behavior). Both classes and instances cannot be directly modified. The point is to create an instance of each class upon import, as was done with None .

Both approaches give me a few immutable unique objects that can be used for comparison just like None . For me, the second one looks more NoneType -like, since None is an instance, but I'm not sure if it costs an increase in ideal complexity. Waiting for you from you.

+5
source share
1 answer
Theoretically, this is an interesting exercise. But when you say "although I'm going to use them in a real project," you lose me.

If the real project is very non-lingual (using traits or some other package to emulate static input, using __slots__ so that people don’t fall on sharp objects, etc.) - well, I have nothing for you, because for me it useless but others do.

If the real project is Pythonic, then do the simplest.

Your answer β€œdon't use instances at all” is correct here, but you also don't need to do a lot of class definition.

For example, if you have a function that can take None as a real parameter, and you want to know if the parameter has been set by default, just do the following:

 class NoParameterGiven: pass def my_function(my_parameter=NoParameterGiven): if my_parameter is NoParameterGiven: <do all my default stuff> 

This class is so cheap that there is no reason to share it between files. Just create it where you need it.

Your status classes are a different story, and you can use something like this enum module that @Dunes mentioned - it has some nice features.

OTOH, if you want this to be very simple, you could just do something like this:

 class MyStates: class State1: pass class State2: pass class State3 pass 

You do not need to create any instances, and you can reference them as follows: MyStates.State1 .

+1
source

All Articles