Environment: python3
I'd like to have some generic object with my custom methods and attributes, so I've created a function that internally creates a class and instance from the class from given base class like the following.
But I am not sure how to initialize the instance with the given value. I would appreciate if you have any ideas?
def get_object_with_uuid(value):
base = type(value)
class ObjectAndUUID(base):
def __init__(self):
self.uuid = "dummy"
super().__init__()
return ObjectAndUUID()
if __name__ == '__main__':
a = get_object_with_uuid([4,5,6])
b = get_object_with_uuid((7,8,9))
c = get_object_with_uuid(10)
d = get_object_with_uuid("some_string")
a.append(7)
print(a) # '[7]' -> I want this to be [4,5,6,7]
print(a.uuid) # 'dummy' -> this is expected
print(b) # '()' -> I want this to be (7,8,9)
print(b.uuid) # 'dummy' -> this is expected
print(c) # '0' -> I want this to be 10
print(c.uuid) # 'dummy' -> this is expected
print(d) # '' -> I want this to be 'some_string'
print(d.uuid) # 'dummy' -> this is expected
# and so on...
To my best knowledge, Python does not have a generic type of syntax, and based on the current view of the code, there is no need to inherit str, list or dict in ObkjectAndUUID. I keep it for consistency only.
Passing arguments to the instance is done via __init__. Note that one of the arguments of init is self, so there is no need to create it.
def get_object_with_uuid(value):
base = type(value)
class ObjectAndUUID(base):
def __init__(self, val):
self.uuid = "dummy"
self.val = val
return ObjectAndUUID(value)
Related
I have this code:
class A:
def __init__(self, vals: list):
self._vals = vals
def __len__(self) -> int:
# some side effects like logging maybe
return len(self._vals)
def print_len(self) -> None:
# some function that uses the len above
print(len(self))
class B(A):
def __len__(self) -> int:
return 0
The issue is, I want print_len to always call A.__len__. I can do this:
class A:
def __init__(self, vals: list):
self._vals = vals
def __len__(self) -> int:
return len(self._vals)
def print_len(self) -> None:
print(A.__len__(self))
class B(A):
def __len__(self) -> int:
return 0
But it feels wrong. Basically I want B to lie about __len__ to outside callers, but internally use the correct len specified in A.
So
a = A([1, 2, 3])
print(len(a)) # print 3
a.print_len() # print 3 - no surprises there
b = B([1, 2, 3])
print(len(b)) # print 0 - overload the __len__
b.print_len() # want this to be 3 using A's __len__, not 0 using B's __len__
Is there any way to ensure a class always uses its own version of a method rather than a subclass' version? I thought name mangling of dunder methods would help here.
I think your approach is a good one. The zen of Python states that "There should be one-- and preferably only one --obvious way to do it." and I think you've found it.
That being said, you can do this via name mangling. You just need to prefix the method with double underscores (don't add them to the end like magic methods). This will create a private method which won't ever be overwritten by subclasses.
I think this might be self-defeating since you're now putting the computation in a different method.
class A:
def __init__(self, vals: list):
self._vals = vals
def __len__(self) -> int:
return self.__length()
def __length(self) -> int:
return len(self._vals)
def print_len(self) -> None:
print(self.__length())
I'm looking for a way to dynamically inherit a parent class with its attributes and methods, by using type for class creation and super for inheritance, like so:
class A:
def __init__(self,a,b):
self.a = a
self.b = b
def some_method(self,q):
return (self.a + self.b)**q
def B_init(self,**kwargs):
super().__init__(**kwargs)
def another_method(self,):
return 1
def class_B_factory(parent_class):
return type(
'B',
(parent_class, some_other_parent_class),
{'__init__':B_init,
'another_method':another_method
}
)
And then be able to call...
model = class_B_factory(A)(a = 1, b = 5)
print(model.some_method(2)) # outputs to (1 + 5)**2 = 36
I'm not sure how to proceed. I don't think I'll need a custom metaclass since I'm pretty sure you can't call the parent class' __init__ method while also creating self in the process. I also tried overriding the default __init__ method outside the scope of class_B_factory like so:
def class_B_factory(parent_class):
return type(
'B',
(parent_class, some_other_parent_class),
{'another_method':another_method
}
)
B = class_B_factory(A)
def B_init(self,**kwargs):
super(B,self).__init__(**kwargs)
B.__init__ = B_init
model = B(a = 1, b = 5)
because I figured type doesn't need __init__ right away, as it is only needed during instantiation. But then I get TypeError: __init__() got an unexpected keyword argument error, which seems like it didn't work, and its not clean anyway.
EDIT: I tried defining the methods outside the factory via the following but I am still unsuccessful. Not sure how to fix it. Python has trouble instantiating maybe?
class A:
...
def B_init(self, produced_class = None, **kwargs):
super(produced_class,self).__init__(**kwargs)
def another_method(self, q, parent_class = None):
if parent_class is not None:
return 3 * parent_class.some_method(self,q) # I expect any parent_class passed to have a method called some_method
return 1
def class_B_factory(parent_class, additional_methods):
methods = {}
for name, method in additional_methods.items():
if "parent_class" in signature(method).parameters:
method = partial(method, parent_class = parent_class) # freeze the parent_class argument, which is a cool feature
methods[name] = method
newcls = type(
'B',
(parent_class,),
methods # would not contain B_init
)
newcls.__init__ = partial(B_init, produced_class = newcls) # freeze the produced class that I am trying to fabricate into B_init here
return newcls
model = class_B_factory(parent_class = A, additional_methods = {"another_method": another_method})
print(signature(model.__init__).parameters) # displays OrderedDict([('self', <Parameter "self">),...]) so it contains self!
some_instance_of_model = model(a = 1, b = 5) # throws TypeError: B_init() missing 1 required positional argument: 'self'
The parameterless form of super() relies on it being physically placed inside a class body - the Python machinnery them will, under the hood, create a __class__ cell variable referring that "physical" class (roughly equivalent to a non-local variable), and place it as the first parameter in the super() call.
For methods not written inside class statements, one have to resort to explicitly placing the parameters to super, and these are the child class, and the instance (self).
The easier way to do that in your code is to define the methods inside your factory function, so they can share a non-local variable containing the newly created class in the super call:
def class_B_factory(parent_class):
def B_init(self,**kwargs):
nonlocal newcls # <- a bit redundant, but shows how it is used here
super(newcls, self).__init__(**kwargs)
def another_method(self,):
return 1
newcls = type(
'B',
(parent_class, some_other_parent_class),
{'__init__':B_init,
'another_method':another_method
}
return newcls
If you have to define the methods outside of the factory function (which is likely), you have to pass the parent class into them in some form. The most straightforward would be to add a named-parameter (say __class__ or "parent_class"), and use functools.partial inside the factory to pass the parent_class to all methods in a lazy way:
from functools import partial
from inspect import signature
class A:
...
# the "parent_class" argument name is given a special treatement in the factory function:
def B_init(self, *, parent_class=None, **kwargs):
nonlocal newcls # <- a bit redundant, but shows how it is used here
super([parent_class, self).__init__(**kwargs)
def another_method(self,):
return 1
def class_B_factory(parent_class, additional_methods, ...):
methods = {}
for name, method in additional_methods.items():
if "parent_class" in signature(method).parameters:
method = partial(method, parent_class=parent_class)
# we populate another dict instead of replacing methods
# so that we create a copy and don't modify the dict at the calling place.
methods[name] = method
newcls = type(
'B',
(parent_class, some_other_parent_class),
methods
)
return newcls
new_cls = class_B_factory(B, {"__init__": B_init, "another_method": another_method})
When I define a class, I often want to set a collection of attributes for that class upon object creation. Until now, I have done so by passing the attributes as arguments to the init method. However, I have been unhappy with the repetitive nature of such code:
class Repository(OrderedDict,UserOwnedObject,Describable):
def __init__(self,user,name,gitOriginURI=None,gitCommitHash=None,temporary=False,sourceDir=None):
self.name = name
self.gitOriginURI = gitOriginURI
self.gitCommitHash = gitCommitHash
self.temporary = temporary
self.sourceDir = sourceDir
...
In this example, I have to type name three times, gitOriginURI three times, gitCommitHash three times, temporary three times, and sourceDir three times. Just to set these attributes. This is extremely boring code to write.
I've considered changing classes like this to be along the lines of:
class Foo():
def __init__(self):
self.a = None
self.b = None
self.c = None
And initializing their objects like:
f = Foo()
f.a = whatever
f.b = something_else
f.c = cheese
But from a documentation standpoint, this seems worse, because the user of the class then needs to know which attributes need to be set, rather than simply looking at the autogenerated help() string for the class's initializer.
Are there any better ways to do this?
One thing that I think might be an interesting solution, would be if there was a store_args_to_self() method which would store every argument passed to init as an attribute to self. Does such a method exist?
One thing that makes me pessimistic about this quest for a better way, is that looking at the source code for the date object in cPython's source, for example, I see this same repetitive code:
def __new__(cls, year, month=None, day=None):
...
self._year = year
self._month = month
self._day = day
https://github.com/python/cpython/blob/master/Lib/datetime.py#L705
And urwid, though slightly obfuscated by the use of setters, also has such "take an argument and set it as an attribute to self" hot-potato code:
def __init__(self, caption=u"", edit_text=u"", multiline=False,
align=LEFT, wrap=SPACE, allow_tab=False,
edit_pos=None, layout=None, mask=None):
...
self.__super.__init__("", align, wrap, layout)
self.multiline = multiline
self.allow_tab = allow_tab
self._edit_pos = 0
self.set_caption(caption)
self.set_edit_text(edit_text)
if edit_pos is None:
edit_pos = len(edit_text)
self.set_edit_pos(edit_pos)
self.set_mask(mask)
https://github.com/urwid/urwid/blob/master/urwid/widget.py#L1158
You could use the dataclasses project to have it take care of generating the __init__ method for you; it'll also take care of a representation, hashing and equality testing (and optionally, rich comparisons and immutability):
from dataclasses import dataclass
from typing import Optional
#dataclass
class Repository(OrderedDict, UserOwnedObject, Describable):
name: str
gitOriginURI: Optional[str] = None
gitCommitHash: Optional[str] = None
temporary: bool = False
sourceDir: Optional[str] = None
dataclasses were defined in PEP 557 - Data Classes, which has been accepted for inclusion in Python 3.7. The library will work on Python 3.6 and up (as it relies on the new variable annotation syntax introduced in 3.6).
The project was inspired by the attrs project, which offers some more flexibility and options still, as well as compatibility with Python 2.7 and Python 3.4 and up.
Well, you could do this:
class Foo:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
foo = Foo(a=1, b='two', c='iii')
print(foo.a, foo.b, foo.c)
output
1 two iii
But if you do, it's probably a Good Idea to check that the keys in kwargs are sane before dumping them into your instances __dict__. ;)
Here's a slightly fancier example that does a little bit of checking of the passed-in args.
class Foo:
attrs = ['a', 'b', 'c']
''' Some stuff about a, b, & c '''
def __init__(self, **kwargs):
valid = {key: kwargs.get(key) for key in self.attrs}
self.__dict__.update(valid)
def __repr__(self):
args = ', '.join(['{}={}'.format(key, getattr(self, key)) for key in self.attrs])
return 'Foo({})'.format(args)
foo = Foo(a=1, c='iii', d='four')
print(foo)
output
Foo(a=1, b=None, c=iii)
For Python 2.7 my solution is to inherit from namedtuple and use namedtuple itself as only argument to init. To avoid overloading new every time we can use decorator. The advantage is that we have explicit init signature w/o *args, **kwargs and, so, nice IDE suggestions
def nt_child(c):
def __new__(cls, p): return super(c, cls).__new__(cls, *p)
c.__new__ = staticmethod(__new__)
return c
ClassA_P = namedtuple('ClassA_P', 'a, b, foo, bar')
#nt_child
class ClassA(ClassA_P):
def __init__(self, p):
super(ClassA, self).__init__(*p)
self.something_more = sum(p)
a = ClassA(ClassA_P(1,2,3,4)) # a = ClassA(ClassA_P( <== suggestion a, b, foo, bar
print a.something_more # print a. <== suggesion a, b, foo, bar, something_more
I'll just leave another one recipe here. attrs is useful, but have cons, main of which is lack of IDE suggestions for class __init__.
Also it's fun to have initialization chains, where we use instance of parent class as first arg for __init__ instead of providing all it's attrs one by one.
So I propose the simple decorator. It analyses __init__ signature and automatically adds class attributes, based on it (so approach is opposite to attrs's one). This gave us nice IDE suggestions for __init__ (but lack of suggestions on attributes itself).
Usage:
#data_class
class A:
def __init__(self, foo, bar): pass
#data_class
class B(A):
# noinspection PyMissingConstructor
def __init__(self, a, red, fox):
self.red_plus_fox = red + fox
# do not call parent constructor, decorator will do it for you
a = A(1, 2)
print a.__attrs__ # {'foo': 1, 'bar': 2}
b = B(a, 3, 4) # {'fox': 4, 'foo': 1, 'bar': 2, 'red': 3, 'red_plus_fox': 7}
print b.__attrs__
Source:
from collections import OrderedDict
def make_call_dict(f, is_class_method, *args, **kwargs):
vnames = f.__code__.co_varnames[int(is_class_method):f.__code__.co_argcount]
defs = f.__defaults__ or []
d = OrderedDict(zip(vnames, [None] * len(vnames)))
d.update({vn: d for vn, d in zip(vnames[-len(defs):], defs)})
d.update(kwargs)
d.update({vn: v for vn, v in zip(vnames, args)})
return d
def data_class(cls):
inherited = hasattr(cls, '_fields')
if not inherited: setattr(cls, '_fields', None)
__init__old__ = cls.__init__
def __init__(self, *args, **kwargs):
d = make_call_dict(__init__old__, True, *args, **kwargs)
if inherited:
# tricky call of parent __init__
O = cls.__bases__[0] # put parent dataclass first in inheritance list
o = d.values()[0] # first arg in my __init__ is parent class object
d = OrderedDict(d.items()[1:])
isg = o._fields[O] # parent __init__ signature, [0] shows is he expect data object as first arg
O.__init__(self, *(([o] if isg[0] else []) + [getattr(o, f) for f in isg[1:]]))
else:
self._fields = {}
self.__dict__.update(d)
self._fields.update({cls: [inherited] + d.keys()})
__init__old__(self, *args, **kwargs)
cls.__attrs__ = property(lambda self: {k: v for k, v in self.__dict__.items()
if not k.startswith('_')})
cls.__init__ = __init__
return cls
here is my basic code.
from typing import Optional
class State:
name = 'unnamed state'
def __init__(self, name: str = None) -> None:
super().__init__()
if name is not None:
self.name = name
def on_enter(self) -> None:
pass
def on_leave(self) -> None:
pass
def to(self, dest: Optional['State']) -> Optional['State']:
self.on_leave()
if dest is not None:
dest.on_leave()
return dest
class AState(State):
pass
class BState(State):
def b_func(self):
pass
a = AState()
b = BState()
a.to(b).b_func()
currently type hint shows the type of a.to(b) is Optional[State], but what I wanted is to return the parameter is self, which is BState. Is this available in the type system in Python?
If you'd like to have a function always return exactly the same type as its parameter, you can use generics via the TypeVars class:
from typing import Optional, TypeVar
# Note that "TState" is an arbitrary name.
TState = TypeVar('TState', bound='State')
class State:
name = 'unnamed state'
def __init__(self, name: str = None) -> None:
super().__init__()
if name is not None:
self.name = name
def on_enter(self) -> None:
pass
def on_leave(self) -> None:
pass
def to(self, dest: Optional[TState]) -> Optional[TState]:
self.on_leave()
if dest is not None:
dest.on_leave()
return dest
class AState(State):
pass
class BState(State):
def b_func(self):
pass
a = AState()
b = BState()
a.to(b).b_func()
The expression TState = TypeVar('TState', bound=State) means "create a new generic parameter named TState which must always be a subclass of a State object." However, since the State class isn't defined yet, we need to use a forward reference and have the bound be a string instead of the class name: TState = TypeVar('TState', bound='State').
You could also do TState = TypeVar('TState') which means "create a new generic parameter named TState which can be anything", but that probably isn't what you want so I don't recommend it.
You can learn more about upper bounds in TypeVars here: http://mypy.readthedocs.io/en/latest/generics.html#type-variables-with-upper-bounds
In Python3, instance methods can be called in two ways, obj.ix() or Foo.ix(obj). Setting aside whether it is a good idea or not: When using the latter, is there a way to get the class that the instance method was accessed through?
class Foo(object):
#classmethod
def cx(cls, obj):
print(cls.X)
def ix(self):
# Any way to get the class that ix was accessed through?
print(self.X)
class AFoo(Foo):
X = "A"
class BFoo(Foo):
X = "B"
a = AFoo()
AFoo.cx(a) # Prints "A"
AFoo.ix(a) # Prints "A"
b = BFoo()
BFoo.cx(b) # Prints "B"
BFoo.ix(b) # Prints "B"
AFoo.cx(b) # Prints "A"
AFoo.ix(b) # Prints "B" -> I would like "A", like classmethod.
BFoo.cx(a) # Prints "B"
BFoo.ix(a) # Prints "A" -> I would like "B", like classmethod.
As you can see, the desired behavior is trivial to achieve with a class method, but there does not appear to be a way to do the same with an instance method.
Nope. This information is not preserved. If you want that info, you'd have to write a custom descriptor to implement a new method type. For example:
import functools
class CrazyMethod:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return functools.partial(self.func, owner)
return functools.partial(self.func, instance, instance)
class Foo:
#CrazyMethod
def foo(accessed_through, self):
print(accessed_through)
class Bar(Foo): pass
obj = Bar()
obj.foo() # <__main__.Bar object at 0xb727dd4c>
Bar.foo(obj) # <class '__main__.Bar'>
Foo.foo(obj) # <class '__main__.Foo'>
I have already accepted user2357112's answer, but just in case anyone is interested I found another way to do it (based on A class method which behaves differently when called as an instance method?):
import types
class Foo(object):
#classmethod
def x(cls, obj):
print(cls.X)
def __init__(self):
self.x = types.MethodType(type(self).x, self)
class AFoo(Foo):
X = "A"
class BFoo(Foo):
X = "B"
a = AFoo()
b = BFoo()
a.x() # Prints "A"
AFoo.x(a) # Prints "A"
AFoo.x(b) # Prints "A"
b.x() # Prints "B"
BFoo.x(b) # Prints "B"
BFoo.x(a) # Prints "B"