Way to use self in method default argument? - python

For a method of a class I want the following behaviour
>>class A:
>> def __init__(self, x):
>> self.x = x
>> def func(self, x = self.x):
>> print(x)
>>a = A(5)
>>a.func(2)
2
>>a.func()
5
But I get this error for the declaration of func():
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in A
NameError: name 'self' is not defined
Is there a recommended way to achieve this behaviour?

Use a sentinel value; None typically suffices.
class A:
def __init__(self, x):
self.x = x
def func(self, x=None):
if x is None:
x = self.x
print(x)
If, for whatever reason, None could be a valid argument, you can create your own sentinel.
_sentinel = object()
class A:
def __init__(self, x):
self.x = x
def func(self, x=_sentinel):
if x is _sentinel:
x = self.x
print(x)

You cannot refer to self in a function declaration, since at that point self indeed doesn't exist (as the error says). The idiomatic way is:
def func(self, x = None):
if x is None:
x = self.x
print(x)
Or perhaps:
def func(self, x = None):
print(x or self.x)
(Though note that falsey isn't the same as None and may hence behave differently.)

Related

Python method that returns instance of class or subclass while keeping subclass attributes

I'm writing a Python class A with a method square() that returns a new instance of that class with its first attribute squared. For example:
class A:
def __init__(self, x):
self.x = x
def square(self):
return self.__class__(self.x**2)
I would like to use this method in a subclass B so that it returns an instance of B with x squared but all additional attributes of B unchanged (i. e. taken from the instance). I can get it to work by overwriting square() like this:
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
def square(self):
return self.__class__(self.x**2, self.y)
If I don't overwrite the square() method, this little code example will fail because I need to pass a value for y in the constructor of B:
#test.py
class A:
def __init__(self, x):
self.x = x
def square(self):
return self.__class__(self.x**2)
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
#def square(self):
# return self.__class__(self.x**2, self.y)
a = A(3)
a2 = a.square()
print(a2.x)
b = B(4, 5)
b2 = b.square()
print(b2.x, b2.y)
$ python test.py
9
Traceback (most recent call last):
File "test.py", line 20, in <module>
b2 = b.square()
File "test.py", line 6, in square
return self.__class__(self.x**2)
TypeError: __init__() takes exactly 3 arguments (2 given)
Overwriting the method once isn't a problem. But A potentially has multiple methods similar to square() and there might be more sub(sub)classes. If possible, I would like to avoid overwriting all those methods in all those subclasses.
So my question is this:
Can I somehow implement the method square() in A so that it returns a new instance of the current subclass with x squared and all other attributes it needs for the constructor taken from self (kept constant)? Or do I have to go ahead and overwrite square() for each subclass?
Thanks in advance!
I'd suggest implementing .__copy__() (and possibly .__deepcopy__ as well) methods for both classes.
Then your squared can be simple method:
def squared(self):
newObj = copy(self)
newObj.x = self.x **2
return newObj
It will work with inheritance, assuming all child classes have correctly implemented __copy__ method.
EDIT: fixed typo with call to copy()
Full working example:
#test.py
from copy import copy
class A:
def __init__(self, x):
self.x = x
def square(self):
newObj = copy(self)
newObj.x = self.x **2
return newObj
def __copy__(self):
return A(self.x)
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
def __copy__(self):
return B(self.x, self.y)
a = A(3)
a2 = a.square()
print(a2.x)
b = B(4, 5)
b2 = b.square()
print(b2.x, b2.y)
check if the object contains y then return the right class instance:
class A:
x: int
def __init__(self, x):
self.x = x
def square(self):
if hasattr(self, 'y'):
return self.__class__(self.x ** 2, self.y)
return self.__class__(self.x**2)
class B(A):
y: int
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
# def square(self):
# return self.__class__(self.x**2, self.y)

How can i use a function inside of another function

I am currently playing around with classes and functions since i am not familiar with python and i would like to know how i can get addy(self, addx) to call addx.
class test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self, addx):
z = addx() + 10
return z
one = test(1)
print(one.addy())
line 15, in print(one.addy()) TypeError: addy() missing 1
required positional argument: 'addx' Process finished with exit code 1
You need to call self from within a class method.
self.addx()
Also the addx parameter on this line shouldn't be there:
def addy(self, addx):
I think this is what you are going for:
class test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self):
z = self.addx() + 10
return z
one = test(1)
print(one.addy())
You've overcomplicated things by wrapping it in a class. Take it out and it'll work (mostly) the way you expect.
def add10(x):
return x+10
def add20(x):
return add10(add10(x))
Since you've wrapped it in the class you've complicated the namespace. It's no longer called addx or addy, so using those names throws a NameError. You have to use the qualified name instead.
class FooBar():
def __init__(self):
self.x = 10
def addx(self):
return self.x + 10 # Note the `self.` before the attribute...
def addy(self):
return self.addx() + 10 # ...and also before the method name.
Methods are always passed their owning object as a first argument when called, which is why we've got def addx(self): but then call with self.addx()
If you are attempting to relate addx in the signature of addy to the method addx, you can pass the string name of the method and use getattr:
class Test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self, func):
z = getattr(self, func)() + 10
return z
s = Test(3)
print(s.addy('addx'))

Possible in Python - Class method that can retrieve instance if called from instance instead of class?

Thinking about this and I'm wondering if it is possible (and if so, how to make such a decorator etc.) to have a classmethod, that IF called from an instance, can retrieve data on the instance? Perhaps some more clarity on how the staticmethod and classmethod decorators work would be helpful too (looking at the implementation __builtin__.py did not help)
Example use would be:
class A(object):
def __init__(self, y):
self.y = y
#classmethod
def f(cls, x, y=None):
# if y is unspecified, retrieve it from cls which is presumably an instance
# (should throw an error if its a class because y is not set
if y is None:
y = cls.y
return x + y
So that we could do:
>>>A.f(3, 5)
8
>>>a = A(5)
>>>a.f(3)
8
I came up with this below to mimic the behavior but its pretty inconvenient to implement:
class A(object):
def __init__(self, y):
self.y = y
self.f = self.f_
def f_(self, x):
return x + self.y
#classmethod
def f(cls, x, y):
return x + y
To expand on the comments made by #Adirio You could make a decorator that accomplishes this dynamically.
In this particular implementation, when the decorated method is called it will do a partial bind of the provided arguments to the method and uses the method's signature to determine what parameters have not been provided.
For any unspecified argument, if the calling object has an attribute matching the unspecified parameter name, the object's attribute value will be injected into the function.
import inspect
class BindableConstructor(object):
def __init__(self, meth):
self.meth = meth
self.sig = inspect.signature(self.meth)
def __get__(self, obj, klass=None):
if obj is not None:
print('Method ', repr(self.meth), ' called from instance ', repr(obj))
if klass is None:
klass = type(obj)
def newmeth(*args, **kwargs):
ba = self.sig.bind_partial(*args, **kwargs)
ba.apply_defaults()
for paramname in self.sig.parameters:
if paramname not in ba.arguments and hasattr(obj, paramname):
ba.arguments[paramname] = getattr(obj, paramname)
return self.meth(klass, *ba.args, **ba.kwargs)
return newmeth
Then suppose you have the following class using this decorator
class MyClass(object):
def __init__(self, y):
self.y = y
#BindableConstructor
def my_constructor(cls, x, y):
return cls(x + y)
Then the following behavior would be observed
>>> a = MyClass(5)
>>> b = MyClass.my_constructor(3, 2)
>>> b
<__main__.MyClass object at 0x0605C770>
>>> b.y
5
>>> c = b.my_constructor(3) # c.y == b.y + 3
Method <function MyClass.my_constructor at 0x05396420> called from instance <__main__.MyClass object at 0x0605C770>
>>> c.y
8
In this particular case ba.apply_defaults is called before checking the object's attributes to inject. If you want the object's attributes to take precedence over defaults, call ba.apply_defaults after the parameter injection logic.
When you try you example, you get an error saying
AttributeError: type object 'A' has no attribute 'y', because in constructor, you assigned y as an attribute of the object and not of the class.
The trivial fix:
class A(object):
def __init__(self, y):
A.y = y
#classmethod
def f(cls, x, y=None):
# if y is unspecified, retrieve it from cls which is presumably an instance
# (should throw an error if its a class because y is not set
if y is None:
y = cls.y
return x + y
Would indeed solve the error, but as the class will only know one single object at a time, you would get weird result as soon as you use more than one:
>>> A.f(3,5)
8
>>> a = A(5)
>>> a.f(3) # fine till there...
8
>>> b = A(7)
>>> a.f(3) # last created object wins here!
10
So the only foolproof way is to create an attribute with the name of the class function in each object. As you only call a class method, a lamdba is enough:
class A(object):
def __init__(self, y):
self.y = y
self.f = lambda x: A.f(x, y) # declare a shortcut for the class method
#classmethod
def f(cls, x, y=None):
return x + y
You can then safely do:
>>> A.f(3,5)
8
>>> a = A(5)
>>> a.f(3)
8
>>> b = A(7)
>>> a.f(3)
8
>>> b.f(3)
10
Do not forget to handle error cases.
class InstanceAndClassMethod(object):
def __init__(self, f):
self.f = f
def __get__(self, instance, owner=None):
if instance is None:
instance = owner
def newfunc(*args, **kwargs):
return self.f(instance, *args, **kwargs)
return newfunc
class A(object):
def __init__(self, y):
self.y = y
#InstanceAndClassMethod
def f(cls, x, y=None):
try:
y = cls.y if y is None else y
except AttributeError:
raise TypeError("f() missing 1 required positional argument: 'y'")
return x + y
With the help of docs.python.org/3/howto/descriptor.html I came up with this, seems to work:
class CoolerClassMethod(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, klass):
if obj is None:
self_ = klass
else:
self_ = obj
def newfunc(*args, **kwargs):
return self.f(self_, *args, **kwargs)
return newfunc
class A(object):
def __init__(self, y):
self.y = y
#CoolerClassMethod
def f(cls, x, y=None):
y = cls.y if y is None else y
return x + y
Testing:
>>> a = A(5)
>>> A.f(3, 5)
8
>>> a.f(3)
8
>>> A.f(3, 5)
8

immutable objects in Python that can have weak references

I've been subclassing tuple or using namedtuple blissfully for a few years, but now I have a use case where I need a class that can be used as a weak referent. And today I learned tuples don't support weak references.
Is there another way to create an immutable object in Python with a fixed set of attributes? I don't need the numeric indexing or variable width of a tuple.
class SimpleThingWithMethods(object):
def __init__(self, n, x):
# I just need to store n and x as read-only attributes
... ??? ...
I guess this raises the obvious question of why immutable; "Pythonic" code usually just assumes we're all adults here and no one in their right mind would reach into a class and muck with its values if it risks ruining the class invariants. In my case I have a class in a library and I am worried about accidental modification of objects by end-users. The people I work with sometimes make incorrect assumptions about my code and start doing things I did not expect, so it's much cleaner if I can raise an error if they accidentally modify my code.
I'm not so worried about bulletproof immutability; if someone really nefarious wants to go and modify things, ok, fine, they're on their own. I just want to make it hard to accidentally modify my objects.
well, this isn't a great answer but it looks like I can modify the answer in https://stackoverflow.com/a/4828492/44330 --- essentially overriding __setattr__ and __delattr__ to meet my needs at least against accidental modification. (but not as nice as subclassing tuple)
class Point(object):
__slots__ = ('x','y','__weakref__')
def __init__(self, x, y):
object.__setattr__(self, "x", x)
object.__setattr__(self, "y", y)
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return self.x.__hash__() * 31 + self.y.__hash__()
Implementing #Elazar's idea:
class Point(object):
__slots__ = ('x','y','__weakref__')
def __new__(cls, x, y):
thing = object.__new__(cls)
object.__setattr__(thing, "x", x)
object.__setattr__(thing, "y", y)
return thing
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return self.x.__hash__() * 31 + self.y.__hash__()
If you don't worry about isinstance checks, you can strengthen you answer:
def Point(x, y):
class Point(object):
__slots__ = ('x','y','__weakref__')
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return x == other.x and y == other.y
def __hash__(self):
return x.__hash__() * 31 + y.__hash__()
p = Point()
object.__setattr__(p, "x", x)
object.__setattr__(p, "y", y)
return p
I don't really recommend it (every invocation creates a class!), just wanted to note the possibility.
It is also possible to go javascript all the way, and supply __getattr__ that will access the local variables. But that will also slow down access, in addition to creation. Now we don't need these slots at all:
class MetaImmutable:
def __setattr__(self, name, val):
raise TypeError
def Point(x, y):
class Point(object):
__metaclass__ = MetaImmutable
__slots__ = ('__weakref__',)
def __getattr__(self, name):
if name == 'x': return x
if name == 'y': return y
raise TypeError
#property
def x(self): return x
#property
def y(self): return y
def __eq__(self, other):
return x == other.x and y == other.y
def __hash__(self):
return x.__hash__() * 31 + y.__hash__()
return Point()
Test it:
>>> p = Point(1, 2)
>>> p.y
2
>>> p.z
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __getattr__
TypeError
>>> p.z = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> object.__setattr__(p, 'z', 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> from weakref import ref
>>> ref(p)().x
1
>>> type(p).x = property(lambda self: 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __setattr__
TypeError
And finally, you can still break it:
>>> type.__setattr__(type(p), 'x', property(lambda self: 5))
>>> p.x
5
Again, nothing here is recommended. Use #Jasons implementation.
What about using encapsulation and abstraction on the parameter (getter?):
class SimpleThingWithMethods(object):
def __init__(self, n, x):
self._n = n
self._x = x
def x(self):
return self._x
def n(self):
return self._n
SimpleThingWithMethods(2,3).x()
=> 3

Avoid unauthorized value assignment to a python class attribute

Assume a class like this, where attribute x has to be either an integer or a float:
class foo(object):
def __init__(self,x):
if not isinstance(x,float) and not isinstance(x,int):
raise TypeError('x has to be a float or integer')
else:
self.x = x
Assigning a non-integer and non-float to x will return an error when instantiating the class:
>>> f = foo(x = 't')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __init__
TypeError: x has to be a float or integer
But the direct assignment of x does not return any errors:
>>> f = foo(x = 3)
>>> f.x = 't'
>>>
How can I make python raise an error in the latter case?
You could use the Descriptor protocol, although the syntax is a little bit more complicated:
from types import IntType, LongType, FloatType
AllowedTypes = IntType, LongType, FloatType
class NumberDescriptor(object):
def __init__(self, name):
self._name = name
def __set__(self, instance, value):
if not isinstance(value, AllowedTypes):
raise TypeError("%s must be an int/float" % self._name)
instance.__dict__[self._name] = value
class A(object):
x = NumberDescriptor("x")
def __init__(self, x):
self.x = x
if __name__ == "__main__":
a1 = A(1)
print a1.x
a2 = A(1.4)
print a2.x
#a2 = A("1")
a2.x = 4
print a2.x
a1.x = "2"
print a1.x
Use a property:
class Foo(object):
def __init__(self, x):
self.x = x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
if not isinstance(x,float) and not isinstance(x,int):
raise TypeError('x has to be a float or integer')
self._x = x
If you find yourself needing to do this a lot you might want to look into Traits or Traitlets.

Categories

Resources