I have a StringPlus class that represents a string with extra data. I'd like to make it compatible with .join() which is used inside a library that I feed a StringPlus list into. I have no control over the join() call. Simply defining __str__() doesn't work:
class StringPlus:
def __init__(self, string: str, extra_data):
self._string = string
self._extra_data = extra_data
def __str__(self):
return self._string
a = StringPlus("a", [1, 2, 3])
b = "".join([a, "b"])
assert b == "ab"
Any hints?
str.join must be passed an iterable of strings, so this cannot be done without converting each element in the list.
"".join(map(str, [a, "b"]))
# or
"".join(str(x) for x in [a, "b"])
However, changing the class definiton to subclass str can allow this to work without manual conversion.
class StringPlus(str):
Inherit str:
>>> class StringPlus(str):
... def __init__(self, string=''):
... self._extra_data = [1, 2, 3]
...
>>> "".join([StringPlus("a"), "b"])
'ab'
If you need to customize the construction parameters, you also need to overload the __new__ method (note that the parameters in the __new__ and __init__ methods should be consistent):
>>> class StringPlus(str):
... def __new__(cls, string, extra_data):
... return super().__new__(cls, string)
... def __init__(self, string, extra_data):
... self._extra_data = extra_data
...
>>> "".join([StringPlus("a", [1, 2, 3]), "b"])
'ab'
Or just define the __new__ method:
>>> class StringPlus(str):
... def __new__(cls, string, extra_data):
... self = super().__new__(cls, string)
... self._extra_data = extra_data
... return self
...
You can equip your custom StringPlus class with join method to behave like/similar str.join:
class StringPlus:
def __init__(self, string: str):
self._string = string
self._extra_data = [1, 2, 3]
def join(self, args, sep=""):
return sep.join([self._string, *args])
def __str__(self):
return self._string
a = StringPlus("a")
bc = a.join(["b", "c"], sep="")
assert bc == "abc"
Related
Given a class and a set of its methods - how can you determine from within that class which methods have been decorated with a certain decorator?
My goal is to basically get the actual values of the methods that are decorated, so something like:
class A():
def get_values(self):
...
# returned {'a-special-name': 1, 'b': 2}
#my_dec('a-special-name') # Ideally be able to also put optional name
def a(self):
return 1
#my_dec
def b(self):
return 2
Any idea on how to accomplish this?
Edit: this should also work on parent classes, that is, if A is a subclass of:
class B():
#my_dec
def c(self):
return 3
then get_values() of A instance should return {'a-special-name': 1, 'b': 2, 'c': 3} (order is irrelevant of course)
Edit: class based decorator that works but not with inheritance. Any idea how to make it work with inheritance but without having to decorate the class itself?
class my_dec(object):
def __init__(self, func, name=None):
self.func = func
self.name = name or func.__name__
self.func._some_flag = True
def __get__(self, instance, cls=None):
if instance is None:
return self
return self.func(instance)
If you can define the decorator yourself, then simply have it "mark" the method object in some way:
def my_dec(method):
method._this_be_decorated = True
return method
The class can then look for those marked methods; something like:
from inspect import isfunction
class A:
def get_values(self):
return filter(lambda i: isfunction(i) and hasattr(i, '_this_be_decorated'),
vars(type(self)).values())
This will return an iterable of function objects which you can process further as needed.
def my_dec(name):
if callable(name):
# name is callable – take its name
func = name # no make the code more readable
func.special_name = func.__name__
return func
else:
# name is the name to give – add an inner layer of functions
def inner(function_object):
function_object.special_name = name
return function_object
return inner
class A():
def get_values(self):
# return a dict of special name to call result mapping for every class member that has a special_name.
return {func.special_name: func(self) for func in self.__class__.__dict__.values() if hasattr(func, 'special_name')}
# returned {'a-special-name': 1, 'b': 2}
#my_dec('a-special-name') # Ideally be able to also put optional name
def a(self):
return 1
#my_dec
def b(self):
return 2
def no_dec(self):
return 42
should do what you want.
As deceze mentions, decorators can do whatever they want so there's no reliable generic answer. If you "own" the decorator you can add special properties to it's return value ie (Q&D py2.7 example):
def mydec(name=''):
# py27 hack - for py3 you want nonlocal instead
n = [name]
def innerdec(func):
# py27 hack - for py3 you want nonlocal instead
name = n[0] or func.__name__
def wrapper(*args, **kw):
print("in mydec.wrapper for {}".format(name))
return func(*args, **kw)
wrapper.ismydec = True # so we know this is decorated by mydec
wrapper.func = func # so we can get the original func
wrapper.name = name
return wrapper
return innerdec
def collect_dec(cls):
decorated = {}
for attname in dir(cls):
obj = getattr(cls, attname)
if getattr(obj, "ismydec", False):
decorated[obj.name] = obj.func
cls._decorated_funcs = decorated
return cls
#collect_dec
class A():
def get_values(self):
return {
name:func(self) for name, func in self._decorated_funcs.items()
}
#mydec('a-special-name') # Ideally be able to also put optional name
def a(self):
return 1
#mydec() # no name
def b(self):
return 2
a = A()
print(a.get_values())
Which outputs:
{'a-special-name': 1, 'b': 2}
I have a class, that is used to translate binary stream to human readable. I want to translate it both ways, because I send and receive binary messages. Attributes of this class are made mostly the same way - take the bytes from startbyte to stopbyte and decode them - so I made a decision to use a property to do that. But can I make a general "property" that will be used when defining my class attributes?
class Packet(object):
def __init__(self, data):
self.data = data
def standard_getter(startbyte, stopbyte):
def getter(self):
return decode(self.data[startbyte:stopbyte])
return getter
def standard_setter(startbyte, stopbyte):
def setter(self, value):
self.data[startbyte:stopbyte] = encode(value)
return setter
# the way I define properties by now:
protocol_type = property(standard_getter(16, 18), standard_setter(16, 18))
protocol_sub_type = property(standard_getter(18, 20), standard_setter(18, 20))
# the way I would like to do it:
protocol_type = property(standard_property(16, 18))
# or
protocol_type = standard_property(16, 18)
I tried to define a function, that takes two arguments and returns property(getter, setter), but always I'm stuck in giving "self" instance to the function. Is there a nice way I can make it?
Have your function produce both the getter and setter, and return the property object for those two functions:
def standard_property(startbyte, stopbyte):
def getter(self):
return decode(self.data[startbyte:stopbyte])
def setter(self, value):
self.data[startbyte:stopbyte] = encode(value)
return property(getter, setter)
Then use the return value directly:
protocol_type = standard_property(16, 18)
protocol_sub_type = standard_property(18, 20)
Note that the standard_property() function doesn't even need to live in your class; it could be a top-level function too:
>>> def standard_property(startbyte, stopbyte):
... def getter(self):
... return decode(self.data[startbyte:stopbyte])
... def setter(self, value):
... self.data[startbyte:stopbyte] = encode(value)
... return property(getter, setter)
...
>>> encode = lambda v: list(v)
>>> decode = lambda v: ''.join(v)
>>> class Packet(object):
... def __init__(self, data):
... self.data = data
... protocol_type = standard_property(16, 18)
... protocol_sub_type = standard_property(18, 20)
...
>>> p = Packet(list('foo bar baz spam ham eggs'))
>>> p.protocol_type
' h'
>>> p.protocol_sub_type
'am'
>>> p.protocol_type = '_c'
>>> p.protocol_sub_type = 'an'
>>> ''.join(p.data)
'foo bar baz spam_can eggs'
I'd like to represent an object as a string so that it can be accessed both as a dictionary key and as an object in itself. i.e.
class test(object):
def __init__(self, name, number_array):
self.name = name
self.number_array = number_array
self.graph= barChart(number_array)
sample_obj = test('test_object', [(x1,y1), (x2,y2)etc.])
but so that {sample_obj: another_object} would look like {'test_object': another_object}
while still making something like this possible:
for key, val in sample_dict.items(): print(key.name, key.graph)
as well as:
>>> sample_dict['test_object']
another_object
You must define eq that returns positive when comparing with the string i.e.:
def __eq__(self, other):
if self.name == other:
return True
... continue with comparison ...
You must also define hash that returns the same hash as the compared string:
def __hash__(self):
return hash(self.name)
UPDATE: The following code does exactly what the author wanted:
class test(object):
def __init__(self, name, number_array):
self.name = name
self.number_array = number_array
#self.graph= barChart(number_array)
def __eq__(self, other):
try:
return (self.name == other.name) and (self.number_array == other.number_array)
except AttributeError:
return self.name == other
def __hash__(self):
return hash(self.name)
sample_obj = test('test_object', [(0, 1), (2, 3)])
dict1 = {sample_obj: "Hurray"}
print("dict1[sample_obj]", dict1[sample_obj])
print("dict1['test_object']", dict1['test_object'])
dict2 = {'test_object': "Yippie"}
print("dict2[sample_obj]", dict2[sample_obj])
print("dict2['test_object']", dict2['test_object'])
To use a class as a dictionary key, implement __hash__ and __eq__. To change how it appears when you print the dictionary, implement __repr__:
class Test(object):
def __init__(self, name, number_array):
self.name = name
self.number_array = number_array
self.graph = barChart(number_array)
def __eq__(self, other):
return self.name == other.name and self.number_array == other.number_array
def __hash__(self):
return hash(self.name) ^ hash(self.number_array)
def __repr__(self):
return "test_object"
In use:
>>> t = Test("foo", (1, 2, 3))
>>> d = {t: [1, 2, 3]}
>>> t
test_object
>>> d
{test_object: [1, 2, 3]}
>>> d[t]
[1, 2, 3]
Note that this means that both the name and number_array attributes must be hashable - I have used a string and a tuple to ensure this. Also, it is better if __repr__ represents the actual object more closely, e.g.
def __repr__(self):
return "Test({0.name!r}, {0.number_array!r})".format(self)
I am defining a base class in python like
class Base(object):
def __init__(self):
self._changed = False
and some derived classes:
class Car(Base):
def set_type(self, type_):
# do something
def set_mileage(self, mileage):
# do something
class Flower(base):
def set_name(self, name):
# do something
In this example I now want to set the attribute '_changed' to Truewhenever I call a set method of one of the derived classes. I simply could add the line
self._changed = True
to every set method, or use a decorator, but I am looking for a more convenient and automatic way to do this whenever a method is called whose name starts with 'set_'. I am thinking using __getattribute__ like in the following not tried (and not working example:
def __getattribute__(self, name):
if name.startswith('set_'):
self._changed = True
return self.__getattribute__(name)
So how to implement this in the correct way?
Update: A fully working example this time using a metaclass and descriptor with both setter and a getter:
class Field(object):
def __get__(self, ins, type):
return getattr(ins, self.field_name, None)
def __set__(self, ins, val):
setattr(ins, self.field_name, val)
ins._changed = True
class Meta(type):
def __new__(cls, clsname, bases, dct):
for k, v in dct.items():
if isinstance(v, Field):
v.field_name = '_' + k
return type.__new__(cls, clsname, bases, dct)
class Base(object):
__metaclass__ = Meta
def __init__(self):
self._changed = False
class Car(Base):
type = Field()
mileage = Field()
class Flower(Base):
name = Field()
Demo:
>>> c = Car()
>>> c._changed
False
>>> c.type = "4X4"
>>> c._changed
True
>>> c1 = Car()
>>> c1._changed
False
>>> c1.mileage = 100
>>> c1._changed
True
>>> c.type
'4X4'
>>> c1.mileage
100
>>> f = Flower()
>>> f._changed
False
>>> f.name = "Rose"
>>> f._changed
True
>>> f.name
'Rose'
I would use a decorator for this. Something like this (not tested):
def isGet(func):
def newFunc(self, var):
self._changed = True
func(self, var)
return
return newFunc
And then in any get method you want this behaviour, you simply do
#isGet
def set_mileage(self, mileage):
# dosomething
A metaclass would work here:
from types import FunctionType
from functools import wraps
class Setter(type):
def __new__(cls, clsname, bases, dct):
for item in dct:
if item.startswith("set_") and isinstance(dct[item], FunctionType):
dct[item] = cls.changer(dct[item])
return super(Setter, cls).__new__(cls, clsname, bases, dct)
#staticmethod
def changer(func):
#wraps(func)
def wrapper(self, *args, **kwargs):
self._changed = True
return func(self, *args, **kwargs)
return wrapper
class Base(object):
__metaclass__ = Setter
def __init__(self):
self._changed = False
Then just inherit from Base like you normally would.
Sample usage:
>>> from meta import Car
>>> c = Car()
>>> c._changed
False
>>> c.set_type("blah")
ok
>>> c._changed
True
The metaclass is just automatically decorating any method in your class' __dict__ that starts with set_.
Is there any way to avoid calling __init__ on a class while initializing it, such as from a class method?
I am trying to create a case and punctuation insensitive string class in Python used for efficient comparison purposes but am having trouble creating a new instance without calling __init__.
>>> class String:
def __init__(self, string):
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
def __simple(self):
letter = lambda s: ''.join(filter(lambda s: 'a' <= s <= 'z', s))
return filter(bool, map(letter, map(str.lower, self.__string)))
def __eq__(self, other):
assert isinstance(other, String)
return self.__simple == other.__simple
def __getitem__(self, key):
assert isinstance(key, slice)
string = String()
string.__string = self.__string[key]
string.__simple = self.__simple[key]
return string
def __iter__(self):
return iter(self.__string)
>>> String('Hello, world!')[1:]
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
String('Hello, world!')[1:]
File "<pyshell#1>", line 17, in __getitem__
string = String()
TypeError: __init__() takes exactly 2 positional arguments (1 given)
>>>
What should I replace string = String(); string.__string = self.__string[key]; string.__simple = self.__simple[key] with to initialize the new object with the slices?
EDIT:
As inspired by the answer written below, the initializer has been edited to quickly check for no arguments.
def __init__(self, string=None):
if string is None:
self.__string = self.__simple = ()
else:
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
When feasible, letting __init__ get called (and make the call innocuous by suitable arguments) is preferable. However, should that require too much of a contortion, you do have an alternative, as long as you avoid the disastrous choice of using old-style classes (there is no good reason to use old-style classes in new code, and several good reasons not to)...:
class String(object):
...
bare_s = String.__new__(String)
This idiom is generally used in classmethods which are meant to work as "alternative constructors", so you'll usually see it used in ways such as...:
#classmethod
def makeit(cls):
self = cls.__new__(cls)
# etc etc, then
return self
(this way the classmethod will properly be inherited and generate subclass instances when called on a subclass rather than on the base class).
A trick the standard pickle and copy modules use is to create an empty class, instantiate the object using that, and then assign that instance's __class__ to the "real" class. e.g.
>>> class MyClass(object):
... init = False
... def __init__(self):
... print 'init called!'
... self.init = True
... def hello(self):
... print 'hello world!'
...
>>> class Empty(object):
... pass
...
>>> a = MyClass()
init called!
>>> a.hello()
hello world!
>>> print a.init
True
>>> b = Empty()
>>> b.__class__ = MyClass
>>> b.hello()
hello world!
>>> print b.init
False
But note, this approach is very rarely necessary. Bypassing the __init__ can have some unexpected side effects, especially if you're not familiar with the original class, so make sure you know what you're doing.
Using a metaclass provides a nice solution in this example. The metaclass has limited use but works fine.
>>> class MetaInit(type):
def __call__(cls, *args, **kwargs):
if args or kwargs:
return super().__call__(*args, **kwargs)
return cls.__new__(cls)
>>> class String(metaclass=MetaInit):
def __init__(self, string):
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
def __simple(self):
letter = lambda s: ''.join(filter(lambda s: 'a' <= s <= 'z', s))
return filter(bool, map(letter, map(str.lower, self.__string)))
def __eq__(self, other):
assert isinstance(other, String)
return self.__simple == other.__simple
def __getitem__(self, key):
assert isinstance(key, slice)
string = String()
string.__string = self.__string[key]
string.__simple = self.__simple[key]
return string
def __iter__(self):
return iter(self.__string)
>>> String('Hello, world!')[1:]
<__main__.String object at 0x02E78830>
>>> _._String__string, _._String__simple
(('world!',), ('world',))
>>>
Addendum:
After six years, my opinion favors Alex Martelli's answer more than my own approach. With meta-classes still on the mind, the following answer shows how the problem can be solved both with and without them:
#! /usr/bin/env python3
METHOD = 'metaclass'
class NoInitMeta(type):
def new(cls):
return cls.__new__(cls)
class String(metaclass=NoInitMeta if METHOD == 'metaclass' else type):
def __init__(self, value):
self.__value = tuple(value.split())
self.__alpha = tuple(filter(None, (
''.join(c for c in word.casefold() if 'a' <= c <= 'z') for word in
self.__value)))
def __str__(self):
return ' '.join(self.__value)
def __eq__(self, other):
if not isinstance(other, type(self)):
return NotImplemented
return self.__alpha == other.__alpha
if METHOD == 'metaclass':
def __getitem__(self, key):
if not isinstance(key, slice):
raise NotImplementedError
instance = type(self).new()
instance.__value = self.__value[key]
instance.__alpha = self.__alpha[key]
return instance
elif METHOD == 'classmethod':
def __getitem__(self, key):
if not isinstance(key, slice):
raise NotImplementedError
instance = self.new()
instance.__value = self.__value[key]
instance.__alpha = self.__alpha[key]
return instance
#classmethod
def new(cls):
return cls.__new__(cls)
elif METHOD == 'inline':
def __getitem__(self, key):
if not isinstance(key, slice):
raise NotImplementedError
cls = type(self)
instance = cls.__new__(cls)
instance.__value = self.__value[key]
instance.__alpha = self.__alpha[key]
return instance
else:
raise ValueError('METHOD did not have an appropriate value')
def __iter__(self):
return iter(self.__value)
def main():
x = String('Hello, world!')
y = x[1:]
print(y)
if __name__ == '__main__':
main()
Pass another argument to the constructor, like so:
def __init__(self, string, simple = None):
if simple is None:
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
else:
self.__string = string
self.__simple = simple
You can then call it like this:
def __getitem__(self, key):
assert isinstance(key, slice)
return String(self.__string[key], self.__simple[key])
Also, I'm not sure it's allowed to name both the field and the method __simple. If only for readability, you should change that.