In some of my peewee-based ORM models, I need to access the instance as well as the respective class.
Since I caught myself always repeating the line cls = self.__class__
at the top of each such methods, I wrote a decorator for that:
def with_class(function):
"""Adds the instance's class as second
parameter to the wrapped function.
"""
@wraps(function)
def wrapper(self, *args, **kwargs):
"""Wraps the given function."""
return function(self, self.__class__, *args, **kwargs)
return wrapper
Example use case:
class Location(_TerminalModel):
"""Location of a terminal."""
address = CascadingFKField(Address, column_name='address')
annotation = CharField(255, null=True)
...
@with_class
def save_unique(self, cls, *args, **kwargs):
"""Saves the location if it is new or
returns the appropriate existing record.
"""
if self.annotation is None:
annotation_selector = cls.annotation >> None
else:
annotation_selector = cls.annotation == self.annotation
try:
return cls.get((cls.address == self.address) & annotation_selector)
except cls.DoesNotExist:
self.save(*args, **kwargs)
return self
I'd like to have critique on this solution and am open to alternatives.
-
3\$\begingroup\$ So, you saved typing a single line at the expense of typing a single line? :-\ \$\endgroup\$bipll– bipll2018年07月23日 13:19:10 +00:00Commented Jul 23, 2018 at 13:19
-
\$\begingroup\$ @bipll Good point. \$\endgroup\$Richard Neumann– Richard Neumann2018年07月23日 13:26:28 +00:00Commented Jul 23, 2018 at 13:26
1 Answer 1
I'd recommend not using value.__class__
. This is as it's not guaranteed to return the type.
type(x)
is typically the same asx.__class__
(although this is not guaranteed – a new-style class instance is permitted to override the value returned forx.__class__
).
Take:
class A:
__class__ = 'something'
print(A.__class__) # <class 'type'>
print(A().__class__) # 'something'
print(type(A())) # <class '__main__.A'>
I also think just using type
would be cleaner, and easier to understand.
def save_unique(self, *args, **kwargs):
cls = type(self)