I'm using argspec in a function that takes another function or method as the argument, and returns a tuple like this:
(("arg1", obj1), ("arg2", obj2), ...)
This means that the first argument to the passed function is arg1 and it has a default value of obj1, and so on.
Here's the rub: if it has no default value, I need a placeholder value to signify this. I can't use None, because then I can't distinguish between no default value and default value is None. Same for False, 0, -1, etc. I could make it a tuple with a single element, but then the code for checking it would be ugly, and I can't easily turn it into a dict. So I thought I'd create a None-like object that isn't None, and this is what I've come up with:
class MetaNoDefault(type):
def __repr__(cls):
return cls.__name__
__str__ = __repr__
class NoDefault(object):
__metaclass__ = MetaNoDefault
Now ("arg1", NoDefault)
indicates arg1 has no default value, and I can do things like if obj1 is NoDefault:
etc. The metaclass makes it print as just NoDefault
instead of <class '__main__.NoDefault'>
.
Is there any reason not to do it like this? Is there a better solution?
None is a placeholder value Python's None is lovely. None is a universal placeholder value.
The placeholders inside the string module Python are defined in curly brackets, e.g., “Welcome to Guru99 {}”. format('value here'). The placeholder can be empty {}, or it can have a variable for e.g {name} , or it can have a number index e.g {0} , {1} etc.
The placeholder is defined using curly brackets: {}.
The %d operator is used as a placeholder to specify integer values, decimals, or numbers. It allows us to print numbers within strings or other values. The %d operator is put where the integer is to be specified. Floating-point numbers are converted automatically to decimal values.
My favourite sentinel is Ellipsis
, and if I may quote myself:
it's there, it's an object, it's a singleton, and its name means "lack of", and it's not the overused None (which could be put in a queue as part of normal data flow). YMMV.
I had a similar situation some time ago. Here's what I came up with.
# Works like None, but is also a no-op callable and empty iterable.
class NULLType(type):
def __new__(cls, name, bases, dct):
return type.__new__(cls, name, bases, dct)
def __init__(cls, name, bases, dct):
super(NULLType, cls).__init__(name, bases, dct)
def __str__(self):
return ""
def __repr__(self):
return "NULL"
def __nonzero__(self):
return False
def __len__(self):
return 0
def __call__(self, *args, **kwargs):
return None
def __contains__(self, item):
return False
def __iter__(self):
return self
def next(*args):
raise StopIteration
NULL = NULLType("NULL", (type,), {})
It can also act as a null callable and sequence.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With