Python class methods changing self - python

This isn't for anything I'm working on yet, it's just some test code as I'm just learning class methods and suck. But say I have the following code
class Test(int):
def __init__(self,arg):
self = arg
def thing(self):
self += 10
and going,
foo=Test(12) sets foo to 12. However I want it, so when I do, foo.thing(), foo increases by 10. So far, going foo.thing() just keeps it at 12. How would I change this code to do that.

Because int is a immutable, you cannot magically turn it into a mutable type.
Your methods are no-ops. They change self in the local namespace, by reassigning it to something else. They no longer point to the instance. In other words, both methods leave the original instance unaltered.
You cannot do what you want to do with a subclass of int. You'll have to create a custom class from scratch using the numeric type hooks instead.
Background: int has a __new__ method that assigns the actual value to self, and there is no __iadd__ method to support in-place adding. The __init__ method is not ignored, but you can leave it out altogether since __new__ already did the work.
Assigning to self means you just replaced the reference to the instance with something else, you didn't alter anything about self:
>>> class Foo(int):
... def __init__(self, value=0):
... print self, type(self)
... self = 'foobar'
... print type(self)
...
>>> foo = Foo(10)
10 <class '__main__.Foo'>
<type 'str'>
>>> print foo, type(foo)
10 <class '__main__.Foo'>
Because int does not have a __iadd__ in-place add method, your self += 2 is interpreted as self = self + 2 instead; again, you are assigning to self and replacing it with a new value altogether.

Related

When to use type() instead of isinstanceof() in python? [duplicate]

This question already has answers here:
What are the differences between type() and isinstance()?
(8 answers)
Closed 2 years ago.
From what I read googling, it seems that isinstanceof() is always better than type().
What are some situations when using type() is better than isinstanceof() in python?
I am using python 3.7.
They do two different things, you can't really compare them directly. What you've probably read is that you should prefer isinstance when checking the type of an object at runtime. But that isn't the only use-case for type (that is the use-case for isinstance, as its name implies).
What may not be obvious is that type is a class. You can think of "type" and "class" as synonymous. Indeed, it is the class of class objects, a metaclass. But it is a class just like int, float, list, dict etc. Or just like a use-defined class, class Foo: pass.
In its single argument form, it returns the class of whatever object you pass in. This is the form that can be used for type-checking. It is essentially equivalent to some_object.__class__.
>>> "a string".__class__
<class 'str'>
>>> type("a string")
<class 'str'>
Note:
>>> type(type) is type
True
You might also find this form useful if you ever wanted access to the type of an object itself for other reasons.
In its three-argument form, type(name, bases, namespace) it returns a new type object, a new class. Just like any other type constructor, just like list() returns a new list.
So instead of:
class Foo:
bar = 42
def __init__(self, val):
self.val = val
You could write:
def _foo_init(self, val):
self.val = val
Foo = type('Foo', (object,), {'bar':42, '__init__': _foo_init})
isinstance is a function which checks if... an object is an instance of some type. It is a function used for introspection.
When you want to introspect on the type of an object, usually you will probably use isintance(some_object, SomeType), but you might also use type(some_object) is SomeType. The key difference is that isinstance will return True if some_object.__class__ is precisely SomeType or any of the other types SomeType inherits from (i.e. in the method resolution order of SomeType, SomeType.mro()).
So, isinstance(some_object, SomeType) is essentially equivalent to some_object.__class__ is SomeType or some_object.__class__ in SomeType.mro()
Whereas if you use type(some_object) is SomeType, you are only asking some_object.__class__ is SomeType.
Here's a practical example of when you might want to use type instead of isinstance, suppose you wanted to distinguish between int and bool objects. In Python, bool inherits from int, so:
>>> issubclass(bool, int)
True
So that means:
>>> some_boolean = True
>>> isinstance(some_boolean, int)
True
but
>>> type(some_boolean) is int
False
type says the type of variable:
a = 10
type(a)
It will give its type as 'int'
isinstance() says if variable is related to specified type
class b:
def __init__(self):
print('Hi')
c = b()
m = isinstance(c, b)
It will return True because object c is of class type a otherwise it will return False.

How to "wrap" object to automatically call superclass method instead of overriden ones?

Consider:
class A(object):
def f(self): print("A")
class B(A):
def f(self): print("B")
b = B()
I can call A.f on b by doing:
A.f(b)
Is there an easy way to "wrap" b such that wrap(b).f() calls A.f for any f?
Here is my solution which copies the methods from the most upper base class:
import types, copy
def get_all_method_names(clazz):
return [func for func in dir(clazz) if callable(getattr(clazz, func))]
def wrap(obj):
obj = copy.copy(obj)
obj_clazz = obj.__class__
base_clazz = obj_clazz.__bases__[-1] # the one which directly inherits from object
base_methods = get_all_method_names(base_clazz) # list of all method names in base_clazz
for base_method_name in base_methods:
base_method = getattr(base_clazz, base_method_name) # get the method object
if isinstance(base_method, types.FunctionType): # skip dunder methods like __class__, __init__
setattr(obj, base_method_name, base_method) # copy it into our object
return obj
# class declaration from question here
wrapped_b = wrap(b)
wrapped_b.f(wrapped_b) # prints A, unfortunately we have to pass the self parameter explicitly
b.f() # prints B, proof that the original object is untouched
This feels dirty to me, but it also seems to work. I'm not sure I'd rely on this for anything important.
import copy
def upcast(obj, clazz):
if not isinstance(obj, clazz): # make sure we're actually "upcasting"
raise TypeError()
wrapped = copy.copy(obj)
wrapped.__class__ = clazz
return wrapped
This results in
>>> a = A()
>>> a.f()
A
>>> b = B()
>>> b.f()
B
>>> upcast(b, A).f()
A
What I've really done here is essentially monkey-patch a clone of b and lied to it and told it it's actually an A, so when it comes time to resolve which version of f to call, it'll call the one from A.
Object Slicing is not supported in python the way it is done in C++ (The link you are pointing to takes a cpp example).
In Python Object Slicing is a rather different thing which means to slice up any object which supports sequence protocol (implements getitem() and len() methods).
Example :
A = [1,2,3,4,5,6,7,8]
print(A[1:3])
But in C++ Object Slicing is just cutting off the properties added by a base class instance when assigned to a parent class variable.

Python 2 type comments - how can I designate that a return type is a type or a subtype of a particular class?

So:
class A(object): pass
class B(A): pass
def class_factory(letter):
"""rtype : type[A]""" # is this correct ?
if letter == 'A': return A # note no parenthesis ! returns the type, not an instance
elif letter == 'B': return B
raise NotImplementedError
Can't seem to find any docs and searching is rather tricky.
Bonus: How would this be achieved in the python 3.5 implementation of type hints ?
The correct usage would be """rtype: type""". You are specifying the type of the object that is returned, and no matter whether you are returning A or B, the actual object you return is an instance of type. You can see this if you run the following:
class A(object): pass
class B(A): pass
print(type(A))
print(type(A()))
print(type(B))
print(type(B()))
Because you are returning a type in every case, the return type should be type. There is no way of specifying that the class object you return has specific properties, since those properties aren't dependent on the type of the object you return, but on the specific instance of the type you return.
Type hinting in doc strings is not standardised. So there a number of answers.
That being said I would say your return type is a function that returns instances of A. That is, a class is a callable that return instances of itself. Which would be something like:
(Foo, Bar) -> A
Where the Foo and Bar are the types of the arguments to the function.
not sure what exactly are you trying to achieve here but something like this can help ..
>>> class A(object):
pass
>>> class B(A):
pass
>>> def test(letter):
if isinstance(letter,A):
Do something if letter is of object A
elif isinstance(letter,B):
Do something if letter is of object B

What really makes an object callable in python [duplicate]

I would like to do the following:
class A(object): pass
a = A()
a.__int__ = lambda self: 3
i = int(a)
Unfortunately, this throws:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'A'
This only seems to work if I assign the "special" method to the class A instead of an instance of it. Is there any recourse?
One way I thought of was:
def __int__(self):
# No infinite loop
if type(self).__int__.im_func != self.__int__.im_func:
return self.__int__()
raise NotImplementedError()
But that looks rather ugly.
Thanks.
Python always looks up special methods on the class, not the instance (except in the old, aka "legacy", kind of classes -- they're deprecated and have gone away in Python 3, because of the quirky semantics that mostly comes from looking up special methods on the instance, so you really don't want to use them, believe me!-).
To make a special class whose instances can have special methods independent from each other, you need to give each instance its own class -- then you can assign special methods on the instance's (individual) class without affecting other instances, and live happily ever after. If you want to make it look like you're assigning to an attribute the instance, while actually assigning to an attribute of the individualized per-instance class, you can get that with a special __setattr__ implementation, of course.
Here's the simple case, with explicit "assign to class" syntax:
>>> class Individualist(object):
... def __init__(self):
... self.__class__ = type('GottaBeMe', (self.__class__, object), {})
...
>>> a = Individualist()
>>> b = Individualist()
>>> a.__class__.__int__ = lambda self: 23
>>> b.__class__.__int__ = lambda self: 42
>>> int(a)
23
>>> int(b)
42
>>>
and here's the fancy version, where you "make it look like" you're assigning the special method as an instance attribute (while behind the scene it still goes to the class of course):
>>> class Sophisticated(Individualist):
... def __setattr__(self, n, v):
... if n[:2]=='__' and n[-2:]=='__' and n!='__class__':
... setattr(self.__class__, n, v)
... else:
... object.__setattr__(self, n, v)
...
>>> c = Sophisticated()
>>> d = Sophisticated()
>>> c.__int__ = lambda self: 54
>>> d.__int__ = lambda self: 88
>>> int(c)
54
>>> int(d)
88
The only recourse that works for new-style classes is to have a method on the class that calls the attribute on the instance (if it exists):
class A(object):
def __int__(self):
if '__int__' in self.__dict__:
return self.__int__()
raise ValueError
a = A()
a.__int__ = lambda: 3
int(a)
Note that a.__int__ will not be a method (only functions that are attributes of the class will become methods) so self is not passed implicitly.
I have nothing to add about the specifics of overriding __int__. But I noticed one thing about your sample that bears discussing.
When you manually assign new methods to an object, "self" is not automatically passed in. I've modified your sample code to make my point clearer:
class A(object): pass
a = A()
a.foo = lambda self: 3
a.foo()
If you run this code, it throws an exception because you passed in 0 arguments to "foo" and 1 is required. If you remove the "self" it works fine.
Python only automatically prepends "self" to the arguments if it had to look up the method in the class of the object and the function it found is a "normal" function. (Examples of "abnormal" functions: class methods, callable objects, bound method objects.) If you stick callables in to the object itself they won't automatically get "self".
If you want self there, use a closure.

Creating a list of objects in Python constructor

I would like to pass a variable into the constructor of an object and depending upon the value get one or more more objects out. Something like:
class MyClass():
def __init__(self,n_objects):
#Should return n_objects of MyClass
Something doesn't seem quite right about it though since I need to return a list (I think) but self is of type "MyClass". What am I missing?
A constructor only returns a single reference of what you are trying to create, so you can't do it this way. What you need is to make this list external from this:
result = [MyClass() for i in xrange(n_objects)]
Then make a wrapper function around that.
Do note that xrange is for python 2, it was renamed to range in python 3.
If you must insist on keeping the "encapsulation" of the class, you can just define a classmethod. Naive example:
>>> class Foo(object):
... def __init__(self):
... self.bar = 1
... #classmethod
... def Copies(cls, n):
... return [cls() for i in xrange(n)]
...
>>> Foo()
<__main__.Foo object at 0x7f2059ac1910>
>>> Foo.Copies(3)
[<__main__.Foo object at 0x7f2059ac19d0>, <__main__.Foo object at 0x7f2059ac1990>, <__main__.Foo object at 0x7f2059ac1a10>]

Categories