Reputation: 172
I am new to Python and I wonder if there is any way to aggregate methods into 'subspaces'. I mean something similar to this syntax:
smth = Something()
smth.subspace.do_smth()
smth.another_subspace.do_smth_else()
I am writing an API wrapper and I'm going to have a lot of very similar methods (only different URI) so I though it would be good to place them in a few subspaces that refer to the API requests categories. In other words, I want to create namespaces inside a class. I don't know if this is even possible in Python and have know idea what to look for in Google.
I will appreciate any help.
Upvotes: 11
Views: 2825
Reputation: 1801
A simple way to do it is with SimpleNamespace
:
from types import SimpleNamespace
class Something:
@staticmethod
def _did_something(input):
print(input)
subspace = SimpleNamespace(
my_var="stuff",
do_something=_did_something
)
In simple use:
>>> f = Something()
>>> f.subspace.my_var
stuff
>>> f.subspace.do_something("hello")
hello
In this case, items which do not require reference to internal class variables can be used with a @staticmethod
definition, and will operate without instantiation as well:
>>> Something.subspace.my_var
stuff
>>> Something.subspace.do_something("goodbye")
goodbye
For more complex operations, where a reference to another value is needed, the class requires instantiation such that the internal parameters can be set:
from types import SimpleNamespace
class Something:
def __init__(self):
# must be assigned after class instantiation
self.other_subspace.tell_secrets = self._other_func
@staticmethod
def _did_something(input):
print(input)
subspace = SimpleNamespace(
my_var="stuff",
do_something=_did_something
)
def _other_func(self,input):
print(self.other_subspace.secret + " and " + input)
other_subspace = SimpleNamespace(
secret="mayonaise",
sauce="duck",
tell_secrets=None #placeholder, set after instantiation
)
In use, the values can then be referenced after object creation:
>>> f = Something()
>>> f.other_subspace.sauce
duck
>>> f.other_subspace.tell_secrets("ketchup")
mayonaise and ketchup
Upvotes: 0
Reputation: 2826
I had this need a couple years ago and came up with this:
class Registry:
"""Namespace within a class."""
def __get__(self, obj, cls=None):
if obj is None:
return self
else:
return InstanceRegistry(self, obj)
def __call__(self, name=None):
def decorator(f):
use_name = name or f.__name__
if hasattr(self, use_name):
raise ValueError("%s is already registered" % use_name)
setattr(self, name or f.__name__, f)
return f
return decorator
class InstanceRegistry:
"""
Helper for accessing a namespace from an instance of the class.
Used internally by :class:`Registry`. Returns a partial that will pass
the instance as the first parameter.
"""
def __init__(self, registry, obj):
self.__registry = registry
self.__obj = obj
def __getattr__(self, attr):
return partial(getattr(self.__registry, attr), self.__obj)
# Usage:
class Something:
subspace = Registry()
another_subspace = Registry()
@MyClass.subspace()
def do_smth(self):
# `self` will be an instance of Something
pass
@MyClass.another_subspace('do_smth_else')
def this_can_be_called_anything_and_take_any_parameter_name(obj, other):
# Call it `obj` or whatever else if `self` outside a class is unsettling
pass
At runtime:
>>> smth = Something()
>>> smth.subspace.do_smth()
>>> smth.another_subspace.do_smth_else('other')
This is compatible with Py2 and Py3. Some performance optimizations are possible in Py3 because __set_name__
tells us what the namespace is called and allows caching the instance registry.
Upvotes: 1
Reputation: 10810
One way to do this is by defining subspace
and another_subspace
as properties that return objects that provide do_smth
and do_smth_else
respectively:
class Something:
@property
def subspace(self):
class SubSpaceClass:
def do_smth(other_self):
print('do_smth')
return SubSpaceClass()
@property
def another_subspace(self):
class AnotherSubSpaceClass:
def do_smth_else(other_self):
print('do_smth_else')
return AnotherSubSpaceClass()
Which does what you want:
>>> smth = Something()
>>> smth.subspace.do_smth()
do_smth
>>> smth.another_subspace.do_smth_else()
do_smth_else
Depending on what you intend to use the methods for, you may want to make SubSpaceClass
a singleton, but i doubt the performance gain is worth it.
Upvotes: 4