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.
Related
class Mod:
def __init__(self, prefix='', name=None, **kws):
self._prefix = prefix
self.opts = kws
self._name = name
def _reprstring(self, long=False):
out = self._name
opts = []
if len(self._prefix) > 0:
opts.append(f"prefix='{self._prefix}'")
if long:
for k, v in self.opts.items():
opts.append(f"{k}='{v}'")
if len(opts) > 0:
out = f"{out}, {', '.join(opts)}"
return f"Model({out})"
#property
def name(self):
return self._reprstring(long=False)
#name.setter
def name(self, value):
self._name = value
a=Mod(prefix='my_prefix',name='my_name',age=14,size='unknown')
a.name='olo'
print(a.name)
In the above code, is the only way to call _reprstring with long=True by typing a._reprstring(long=True)? This effectively makes the if long statement almost never used, then.
Is there no way of doing so while doing print(a.name)? --> I've tried doing print(a.name(long=True)), but I'm getting the error TypeError: 'str' object is not callable. Is this because python thinks I'm trying to call f"Model({out})"?
Preserve your encapsulation of sensitive functions and data while still supporting the functionality users need by implementing another property.
... #property
... def long_name(self):
... return self._reprstring(True)
...
>>> a=Mod(prefix='my_prefix',name='my_name',age=14,size='unknown')
>>> a.name='olo'
>>> print(a.long_name)
Model(olo, prefix='my_prefix', age='14', size='unknown')
>>>
I want to use a weak reference to a namedtuple, but it fails:
>>> import collections
>>> import weakref
>>>
>>> Foo = collections.namedtuple('Foo','a b c')
>>> weakref.ref(Foo(1,2,3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot create weak reference to 'Foo' object
I tried to fix this with __slots__ = ('__weakref__',) but this fails also:
>>> class Foo(collections.namedtuple('Foo','a b c')):
... __slots__ = ('__weakref__',)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
nonempty __slots__ not supported for subtype of 'Foo'
Is there a workaround?
Short answer
Unfortunately, non-empty __slots__ flat-out don't work with any kind of tuple, not just namedtuple().
Work around
The best you can do is to have a class that doesn't inherit from tuple.
I wrote one for you (see below). It is used like this:
from __future__ import print_function
from altnamedtuple import AltNamedTuple
from weakref import proxy
class Point(AltNamedTuple):
__slots__ = ('x', 'y', '__weakref__')
def __init__(self, x, y):
self.x = x
self.y = y
# Now, exercise the capabilities of the named tuple alternative
p = Point(10, 20)
r = proxy(p)
print(len(r)) # sizeable
print(r[0]) # indexed access
print(r.y) # attribute access
print(list(r)) # iterable
x, y = r # unpackable
print(x, y)
print(20 in r) # membership testing
print(tuple(reversed(p))) # reversible
print(r == (10, 20)) # equality
print(r != (30, 40)) # inequality
print(hash(p)) # hashable
print(r) # nice repr
print(r._asdict()) # conversion to a dict
print(r._replace(y=2))
t = (11, 22)
print(r.count(10))
print(r.index(20))
print(Point._make(t)) # alternative constructor
Note, weakrefs proxies don't pass through calls to __hash__ or __reversed__. That's an intrinsic limitation that no class can work around.
Source code for AltNamedTuple
Here's the class that does all the work:
class AltNamedTuple(object):
"Subclasser needs to define: __slots__ and __init__"
__slots__ = ()
def __getattr__(self, attr):
if attr != '_fields': raise AttributeError(attr)
if '__weakref__' in self.__slots__:
return self.__slots__[:-1]
return self.__slots__
def __len__(self):
return len(self._fields)
def __getitem__(self, index):
attr = self._fields[index]
return getattr(self, attr)
def __iter__(self):
for attr in self._fields:
yield getattr(self, attr)
def __reversed__(self):
return iter(reversed(tuple(self)))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __ne__(self, other):
return tuple(self) != tuple(other)
def __hash__(self):
return hash(tuple(self))
def __repr__(self):
pairs = ['%s=%r' % (a, getattr(self, a)) for a in self._fields]
return ('%s(%s)' % (self.__class__.__name__, ', '.join(pairs)))
#classmethod
def _make(cls, iterable):
return cls(*iterable)
def _asdict(self):
return dict(zip(self._fields, self))
def _replace(_self, **kwds):
d = _self._asdict()
d.update(kwds)
return _self.__class__(**d)
def count(self, value):
return tuple(self).count(value)
def index(self, value):
return tuple(self).index(value)
I'd like to create a generalized __eq__() method for the following Class. Basically I'd like to be able to add another property (nick) without having to change __eq__()
I imagine I can do this somehow by iterating over dir() but I wonder if there is a way to create a comprehension that just delivers the properties.
class Person:
def __init__(self, first, last):
self.first=first
self.last=last
#property
def first(self):
assert(self._first != None)
return self._first
#first.setter
def first(self,fn):
assert(isinstance(fn,str))
self._first=fn
#property
def last(self):
assert(self._last != None)
return self._last
#last.setter
def last(self,ln):
assert(isinstance(ln,str))
self._last=ln
#property
def full(self):
return f'{self.first} {self.last}'
def __eq__(self, other):
return self.first==other.first and self.last==other.last
p = Person('Raymond', 'Salemi')
p2= Person('Ray', 'Salemi')
You could use __dict__ to check if everything is the same, which scales for all attributes:
If the objects are not matching types, I simply return False.
class Person:
def __init__(self, first, last, nick):
self.first = first
self.last = last
self.nick = nick
def __eq__(self, other):
return self.__dict__ == other.__dict__ if type(self) == type(other) else False
>>> p = Person('Ray', 'Salemi', 'Ray')
>>> p2= Person('Ray', 'Salemi', 'Ray')
>>> p3 = Person('Jared', 'Salemi', 'Jarbear')
>>> p == p2
True
>>> p3 == p2
False
>>> p == 1
False
You can get all the properties of a Class with a construct like this:
from itertools import chain
#classmethod
def _properties(cls):
type_dict = dict(chain.from_iterable(typ.__dict__.items() for typ in reversed(cls.mro())))
return {k for k, v in type_dict.items() if 'property' in str(v)}
The __eq__ would become something like this:
def __eq__(self, other):
properties = self._properties() & other._properties()
if other._properties() > properties and self._properties() > properties:
# types are not comparable
return False
try:
return all(getattr(self, prop) == getattr(other, prop) for prop in properties)
except AttributeError:
return False
The reason to work with the reversed(cls.mro()) is so something like this also works:
class Worker(Person):
#property
def wage(self):
return 0
p4 = Worker('Raymond', 'Salemi')
print(p4 == p3)
True
you can try to do this, it will also work if you want eq inside dict and set
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(self, other.__class__):
return self.__hash__() == other.__hash__()
return NotImplemented
def __hash__(self):
"""Overrides the default implementation,
and set which fieds to use for hash generation
"""
__make_hash = [
self.first
]
return hash(tuple(sorted(list(filter(None, __make_hash)))))
I want to do this:
a = TestClass1() <br>
a.test.fun() #==> this i want to call TestClass2 method fun() <br>
a.test(a=10).fun() #===> this i want to call TestClass3 method fun() <br>
Does anyone know how to separate this?
I have three classes:
class TestClass1:
aa = ""
def __init__(self):
self.aa = "ccc"
def __getattr__(self, item):
print("test 1 get attr = ",item)
return TestClass2() or TestClass3() #==> I don't how to seperate test and test(a =10)
def __getitem__(self, item):
print("__getitem__",item)
class TestClass2:
def __call__(self, *args, **kwargs):
print("TestClass2 __call__ ")
return self
def fun(self):
print("this TestClass2 fun()")
class TestClass3:
def __call__(self, *args, **kwargs):
print("TestClass3 33333 call 3 ")
return self
def fun(self):
print("this TestClass3 fun()")
in both examples given __getattr__ is called with argument "test".
you need to do something like this:
class TestClass1:
def __getattr__(self, item):
if item == 'test2':
return TestClass2()
elif item == 'test3':
return TestClass3()
a = TestClass1()
a.test2.fun()
a.test3.fun()
EDIT: Let me explain further. Well, in python there is no difference between a function and an attribute, everything in python is an object, all objects are treated the same, be it an integer or a function.
When you do a.test it is lowered to a.__getattr__('test').
And when you do a.test(a=10) it is lowered to a.__getattr__('test')(a=10).
The returned object from a.__getattr__('test') is the same.
In the second case you are fetching the attribute test then calling it with an argument a=10.
EDIT2: What you are trying to do could be achieved this way:
class TestClass1:
test = TestClass2()
class TestClass2:
def __call__(self, a):
if a == 10:
return TestClass3()
def fun():
print("this TestClass2 fun()")
a = TestClass1()
a.test # this is TestClass2
a.test.fun # this is TestClass2.fun
a.test(a=10) # this is TestClass3
a.test(a=10).fun # this is TestClass3.fun
EDIT3: A simpler approach would be making test a function:
class TestClass1:
def test(a=None):
if a is None:
return TestClass2()
if a == 10:
return TestClass3()
a = TestClass1()
a.test().fun # TestClass2.fun
a.test(a=10).fun # TestClass3.fun
I'm trying to subclass str, but having some difficulties due to its immutability.
class DerivedClass(str):
def __new__(cls, string):
ob = super(DerivedClass, cls).__new__(cls, string)
return ob
def upper(self):
#overridden, new functionality. Return ob of type DerivedClass. Great.
caps = super(DerivedClass, self).upper()
return DerivedClass(caps + '123')
derived = DerivedClass('a')
print derived.upper() #'A123'
print type(derived.upper()) #<class '__main__.DerivedClass'>
print derived.lower() #'a'
print type(derived.lower()) #<type 'str'>
For inherited methods that don't require any new functionality, such as derived.lower(), is there a simple, pythonic way to return an object of type DerivedClass (instead of str)? Or am I stuck manually overriding each str.method(), as I did with derived.upper()?
Edit:
#Any massive flaws in the following?
class DerivedClass(str):
def __new__(cls, string):
ob = super(DerivedClass, cls).__new__(cls, string)
return ob
def upper(self):
caps = super(DerivedClass, self).upper()
return DerivedClass(caps + '123')
def __getattribute__(self, name):
att = super(DerivedClass, self).__getattribute__(name)
if not callable(att):
return att
def call_me_later(*args, **kwargs):
result = att(*args, **kwargs)
if isinstance(result, basestring):
return DerivedClass(result)
return result
return call_me_later
Good use for a class decorator -- roughly (untested code):
#do_overrides
class Myst(str):
def upper(self):
...&c...
and
def do_overrides(cls):
done = set(dir(cls))
base = cls.__bases__[0]
def wrap(f):
def wrapper(*a, **k):
r = f(*a, **k)
if isinstance(r, base):
r = cls(r)
return r
for m in dir(base):
if m in done or not callable(m):
continue
setattr(cls, m, wrap(getattr(base, m)))
You can do this by overriding __getattribute__ as Zr40 suggests, but you will need to have getattribute return a callable function. The sample below should give you what you want; it uses the functools.partial wrapper to make life easier, though you could implement it without partial if you like:
from functools import partial
class DerivedClass(str):
def __new__(cls, string):
ob = super(DerivedClass, cls).__new__(cls, string)
return ob
def upper(self):
#overridden, new functionality. Return ob of type DerivedClass. Great.
caps = super(DerivedClass, self).upper()
return DerivedClass(caps + '123')
def __getattribute__(self, name):
func = str.__getattribute__(self, name)
if name == 'upper':
return func
if not callable(func):
return func
def call_me_later(*args, **kwargs):
result = func(*args, **kwargs)
# Some str functions return lists, ints, etc
if isinstance(result, basestring:
return DerivedClass(result)
return result
return partial(call_me_later)
You're both close, but checking for each doesn't extend well to overriding many methods.
from functools import partial
class DerivedClass(str):
def __new__(cls, string):
ob = super(DerivedClass, cls).__new__(cls, string)
return ob
def upper(self):
caps = super(DerivedClass, self).upper()
return DerivedClass(caps + '123')
def __getattribute__(self, name):
if name in ['__dict__', '__members__', '__methods__', '__class__']:
return object.__getattribute__(self, name)
func = str.__getattribute__(self, name)
if name in self.__dict__.keys() or not callable(func):
return func
def call_me_later(*args, **kwargs):
result = func(*args, **kwargs)
# Some str functions return lists, ints, etc
if isinstance(result, basestring):
return DerivedClass(result)
return result
return partial(call_me_later)
(Improvements suggested by jarret hardie in comments.)
You might be able to do this by overriding __getattribute__.
def __getattribute__(self, name):
# Simple hardcoded check for upper.
# I'm sure there are better ways to get the list of defined methods in
# your class and see if name is contained in it.
if name == 'upper':
return object.__getattribute__(self, name)
return DerivedClass(object.__getattribute__(self, name))