Python's `self` argument for class defined wrapped func - python

There is a simplified code I have:
class A:
def foo(*args, **kwargs):
def foo_sub(*args, **kwargs):
print(f"foo args: {args}")
return foo_sub
def bar(*args, **kwargs):
print(f"bar args: {args}")
a = A()
class B:
foo = a.foo(1)
bar = a.bar
a.foo(2)()
a.bar()
B.foo()
B.bar()
B().foo()
B().bar()
And there is output:
foo args: ()
bar args: (<__main__.A object at 0x7f9763e38080>,)
foo args: ()
bar args: (<__main__.A object at 0x7f9763e38080>,)
foo args: (<__main__.B object at 0x7f9763e38828>,)
bar args: (<__main__.A object at 0x7f9763e38080>,)
I need to wrap foo func in A class and I really don't understand why does B().foo() passes self as argument? How can I prevent it?
More complicated code example: https://codehs.com/sandbox/id/python-3-2uVmcT

I don't know what you are trying to do here with all this wrappers and stuff. You might want to look at decorators or context managers to cleanly do this.
https://docs.python.org/3/glossary.html#term-decorator
https://docs.python.org/3/reference/compound_stmts.html#grammar-token-decorator
https://docs.python.org/3/library/functools.html#functools.wraps
https://docs.python.org/3/library/stdtypes.html#context-manager-types
Answering your question, one way of doing this is by defining static methods as also already pointed out by #MisterMiyagi. Generally in programming, static methods are methods of a class that you can call without needing a class instance/object. This means that such methods has no reference to a class instance's/object's state (in the form of class attributes/fields). In Python, ordinary class methods has access to the instance/object itself via this automatic self argument. If you don't want it, define it as a #staticmethod instead.
A static method does not receive an implicit first argument.
Which is by practice named self.
https://docs.python.org/3/library/functions.html#staticmethod
If you access a method (a function defined in a class namespace)
through an instance, you get a special object: a bound method (also
called instance method) object. When called, it will add the self
argument to the argument list.
"Through an instance" as how you did with B() to do B().foo().
https://docs.python.org/3/library/stdtypes.html#methods
Quick code to solve your case (note that as I pointed out earlier, there are better alternatives to this):
class A:
#staticmethod # Or use the inline-decorator stlye as #kaya3 mentioned
def foo(*args, **kwargs):
def foo_sub(*args, **kwargs):
print(f"foo args: {args}")
return foo_sub
#staticmethod
def bar(*args, **kwargs):
print(f"bar args: {args}")
a = A()
class B:
#staticmethod
def foo(*args, **kwargs):
return a.foo(1)(*args, **kwargs)
#staticmethod
def bar(*args, **kwargs):
return a.bar(*args, **kwargs)
a.foo(2)()
a.bar()
B.foo()
B.bar()
B().foo()
B().bar()
Output:
foo args: ()
bar args: ()
foo args: ()
bar args: ()
foo args: ()
bar args: ()
Otherwise if you really badly want to skip the self attribute altogether and hack all the way through, you could trigger obj.method.__func__(*args, **kwargs) as described in https://docs.python.org/3/library/stdtypes.html#methods
WARNING: This suggestion is hackish. Proceed with caution :)

Thanks #kaya3 for giving a good idea with staticmethod and also everyone who replied!
I found a solution with metaclass
class BMeta(type):
def __new__(cls, name, bases, dct):
if isinstance(dct.get("foo"), FunctionType):
dct["foo"] = staticmethod(dct["foo"])
return super().__new__(cls, name, bases, dct)
class B(metaclass=BMeta):
foo = a.foo(1)
bar = a.bar
https://codehs.com/sandbox/id/python-3-BaZXiA

Related

Pythonic way of creating a factory with object creation restriction

Preface: I'm trying to guard against misuse (mostly by myself) and not malicious use (thus the "consenting adults" principle does not apply).
I'm trying to implement something like this:
class Foo(Base):
...
class Bar(Base):
...
class FooBarFactory:
__bar_cache = BarCache()
#classmethod
def createFoo(cls):
return Foo()
#classmethod
def createBar(cls, key):
return cls.__bar_cache.get_or_create(key)
The problem is that I want to restrict Foo and Bar creation to only FooBarFactory's methods. So,
foo = FooBarFactory.createFoo() # OK
foo = Foo() # raise AssertionError('use factory method')
How do I do that? One option that I see is to put Foo and Bar inside the factory class (to ensure that code users know about the factory). But that would produce a bloated class definition. Another option is to do something like this:
class Foo:
_trade_secret = 'super_secret_foo_message_dont_use'
def __init__(self, secret):
assert secret == Foo._trade_secret
...
class FooBarFactory:
...
#classmethod
def createFoo(cls):
# suppress private field access warning here
return Foo(Foo._trade_secret)
But that also looks clumsy and verbose.
Any help is greatly appreciated. Thanks!
If your factory can do it, then everyone else can. There is no solution for this in python as noone has special privileges.
On the other hand, while you can't force people to code properly, you can make it hard for them to screw up:
class Foo:
def __new__(*args, **kwargs):
raise NotImplementedError("Use the factory.")
#classmethod
def _new(cls, *args, **kwargs):
foo = super().__new__(cls)
foo.__init__(*args, **kwargs)
return foo
class Factory:
#staticmethod
def createFoo(*args, **kwargs):
return Foo._new(*args, **kwargs)
Factory.createFoo() # works fine
Foo() # raises an exception
But if your users want to call Foo._new then nothing will stop them from creating an object without "your permission".
You can use sys._getframe(1) to get the caller's frame, where you can obtain the caller's cls local variable and the caller's function name. To make sure someone isn't calling Foo.__new__ from a different class with the same name and the same method name, you can check if the filename of the caller's frame is the same as the filename of the current frame:
import sys
class Foo:
def __new__(cls):
caller_frame = sys._getframe(1)
if 'cls' not in caller_frame.f_locals or \
caller_frame.f_locals["cls"].__name__ != 'FooBarFactory' or \
caller_frame.f_code.co_name != 'createFoo' or \
caller_frame.f_code.co_filename != sys._getframe(0).f_code.co_filename:
raise RuntimeError('Foo must be instantiated via the FooBarFactory.createFoo method.')
print('Foo OK')
return super().__new__(cls)
class FooBarFactory:
#classmethod
def createFoo(cls):
return Foo()
so that:
FooBarFactory.createFoo()
outputs:
Foo OK
and:
Foo()
outputs:
RuntimeError: Foo must be instantiated via the FooBarFactory.createFoo method.
Or since you supposedly control your own file, and the FooBarFactor.createFoo method is supposedly the only caller you have in the file that instantiates Foo, the filename check alone should just be enough:
class Foo:
def __new__(cls):
if sys._getframe(1).f_code.co_filename != sys._getframe(0).f_code.co_filename:
raise RuntimeError('Foo must be instantiated via the FooBarFactory.createFoo method.')
return super().__new__(cls)
I came up with the following solution in addition to the various options in other answers:
class Base:
def __new__(cls, *args, **kwargs):
assert kwargs.get('secret') == Factory._secret, 'use the factory'
return super(Base, cls).__new__(cls)
class Foo(Base):
def __init__(self, param, **kwargs):
self.param = param
class Factory:
_secret = 'super_secret_dont_copy'
#classmethod
def create_foo(cls, param):
return Foo(param=param, secret=cls._secret)
Now,
foo = Foo(23) # AssertionError
foo = Factory.create_foo(23) # OK
The solution allows to minimize extra code for additional sub-classes of Base (the validation code is encapsulated in the Base class), but it has a drawback of having to add **kwargs to all sub-classes' __init__.

Member function decorator and self argument

The following minimal example of a decorator on a member function:
def wrap_function(func):
def wrapper(*args, **kwargs):
print(args)
print(kwargs)
return wrapper
class Foo:
#wrap_function
def mem_fun(self, msg):
pass
foo = Foo()
foo.mem_fun('hi')
outputs:
(<__main__.Foo object at 0x7fb294939898>, 'hi')
{}
So self is one of the args.
However when using a wrapper class:
class WrappedFunction:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
print(args)
print(kwargs)
def wrap_function(func):
return WrappedFunction(func)
class Foo:
#wrap_function
def mem_fun(self, msg):
pass
foo = Foo()
foo.mem_fun('hi')
the output is:
('hi',)
{}
So the self, that references the Foo object, is not accessible in the body of __call__ of the WrappedFunction object.
How can I make it accessible there?
You're losing the reference to your bounded instance by wrapping the function logic (but not the instance) and redirecting it to a class instance - at that point, the class instance's own self applies instead of the wrapped instance method as it gets lost in the intermediary decorator (wrap_function()).
You either have to wrap the call to the wrapped function and pass *args/**kwargs to it, or just make a proper wrapper class instead of adding an intermediary wrapper:
class WrappedFunction(object):
def __call__(self, func):
def wrapper(*args, **kwargs):
print(args)
print(kwargs)
# NOTE: `WrappedFunction` instance is available in `self`
return wrapper
class Foo:
#WrappedFunction() # wrap directly, without an intermediary
def mem_fun(self, msg):
pass
foo = Foo()
foo.mem_fun('hi')
# (<__main__.Foo object at 0x000001A2216CDBA8>, 'hi')
# {}
Sadly, but this might be the only solution as you need it in the __call__ function.
Would suggest checking this out: What is the difference between __init__ and __call__ in Python?
def wrap_function(func):
def wrapper(*args, **kwargs):
x = WrappedFunction(func)
x(*args, **kwargs)
return wrapper

Set the property of a class using decorators on a class method in python

I have a class ABC, inside it there is a function someFunction.
I do not want to mess up with the code of someFunction, thus I wrap it with a #MyDecorator. How can this MyDecorator modify the property of the class ABC when I call someFunction?
class ABC(object):
def __init__(self):
self.someProperty = "Initial value"
#MyDecorator
def someFunction(self):
print "Hello world"
class MyDecorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# ...???
return self.func(*args, **kwargs)
abc = ABC()
abc.someFunction() # When calling someFunction(), abc.someProperty would be updated
You can try:
class MyDecorator(object):
def __init__(self, func):
self.func = func
def __call__(self, instance, *args, **kwargs):
instance.someProperty = "New value"
return self.func(instance, *args, **kwargs)
def __get__(self, instance, owner):
if instance is None:
return self
return self.__call__.__class__(self, instance)
class ABC(object):
def __init__(self):
self.someProperty = "Initial value"
#MyDecorator
def someFunction(self):
print("Hello world")
MyDecorator is now a descriptor since it implements __get__.
When an attribute of any instance of MyDecorator is accessed through some class or an instance of that class, __get__ will be called.
__get__ will receive an instance of that owner object in the parameter instance, from there we can return a new instance of instancemethod that will receives that instance implicitly:
>>> abc.someFunction.__class__
<type 'instancemethod'>
>>> abc.someFunction.__class__.__doc__
'instancemethod(function, instance, class)\n\nCreate an instance method object.'
>>>
It requires a function, basically any callable object will work, because MyDecorator is class, __call__ will revise its own self.
In action:
>>> abc = ABC()
>>> abc.someProperty
'Initial value'
>>> abc.someFunction()
Hello world
>>> abc.someProperty
'New value'
>>>
Note in Python 2.X, MyDecorator must be a new-style class that inherits object or that won't work.
You need to define MyDecorator earlier in the file than where you use it, or you get "NameError: name 'MyDecorator' is not defined".
I was wrong when I said "a decorator should return a function". That applies to functions used as decorators, as in my example. However, when a class is used, it doesn't have to return a function as such. It returns an object in that class. It might be less confusing if "class MyDecorator" were named "class MyModifiedFunction" instead, because an element of the class is used as the decorated function.
Usually, the function returned by the decorator will call the original function. Yours doesn't. It doesn't have to if you want to totally replace someFunction rather than just modify it.
You don't need to make MyDecorator a class. You could make it a function. Here's an example:
def MyDecorator(original_function):
def replacement_function(*args, **kwargs):
args[0].some_property = some_new_value
return(original_function(*args, **kwargs))
return(replacement_function)
Follow this by the first half of your code, where you define class ABC.
Note that when it says return(original_function(*args, **kwargs)) it's running the original function and returning whatever the original function returns. If the original function returned an object, it would return that object. However, when it says return(replacement_function) it's doing something quite different: it's not running the replacement function at that time; it's just returning the function as the return value. A decorator has to return a function.
(#dano: Good point. I fixed it, I think.)
Here's the above suggestion in more complete form:
def MyDecorator(original_function):
def replacement_function(*args, **kwargs):
args[0].some_property = 'Modified value'
return(original_function(*args, **kwargs))
return(replacement_function)
class ABC(object):
def __init__(self):
self.some_property = "Initial value"
#MyDecorator
def some_function(self):
print "Hello world"
abc = ABC()
abc.some_function() # When calling some_function(), abc.some_property wou
ld be updated
print "some property is", abc.some_property
I ran this and it works; it prints
Hello world
some property is Modified value
so this verifies that it did indeed modify the some_property in abc. It's accessed as args[0].some_property, because when the decorated function is called (as an object method), self is passed in as the first argument.

How to decorate all methods in a class? Can I just decorate the class? [duplicate]

This question already has answers here:
Attaching a decorator to all functions within a class
(11 answers)
Closed 5 years ago.
I have several classes and they have same implements name but difference realization. I want to decorate all methods in some classes but others not. I have thought about inheritance, but some classes have some methods do not need to be decorated. The problem is that I don't want to decorate methods one by one, some classes they need to be decorated by a same decorator, Is there any solution to fix it?
Your can start all method that required to be decorated with some prefix and then use something like this:
class Xobject(object):
def __init__(self, decorator):
for method_name in dir(self):
if method_name.startswith("dec_"):
attr = getattr(self, method_name)
wrapped = decorator(attr)
setattr(self, method_name, wrapped)
def dec_me_1(self):
print("In dec_me1")
return 0
def dec_me_2(self):
print("In dec_me2")
return 1
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
x = Xobject(decorator)
x.dec_me_1()
x.dec_me_2()
UPDATE:
You can decorate class by mean of function below. When using Python you should know that class in Python is also object so you could change it and pass it to the other function.
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
def decorate_object(p_object, decorator):
for method_name in dir(p_object):
if method_name.startswith("dec_"):
attr = getattr(p_object, method_name)
wrapped = decorator(attr)
setattr(p_object, method_name, wrapped)
decorate_object(Xobject, decorator)
x = Xobject()
x.dec_me_1()
x.dec_me_2()
Also your can decorate already instantiated object same way:
x = Xobject()
x.dec_me_1()
x.dec_me_2()
decorate_object(x, decorator)
x.dec_me_1()
x.dec_me_2()
I'm sure there are a few approaches to this, but the main leading options are:
Create a custom metaclass, where the __new__ method iterates across the attribute dictionary, identifies methods, and decorates them. See http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/ for an example of Python metaclass programming. Disadvantages: that may be more complex than we'd want to get into here.
Do the same in a regular class's __init__ method. Disadvantages: that only decorates instance methods and not class or static methods, and it's slower because it runs every time you create a new instance.
Do it outside the class:
class Foo(object):
def bar(self):
print 'bar'
for name, ref in vars(Foo):
if callable(ref): ...
Disadvantages: You only get one chance to do it right: at import time. Subclasses don't get modified.
Do it in a class-level decorator. Same disadvantages as doing it outside the class (I think).
At some point you have to be explicit about what gets wrapped and what doesn't.
If I've understood you correctly, I think you could do something like this:
def wrapper(func):
def inner(*args, **kwargs):
print "%s was called" func.__name__
return func(*args, **kwargs)
return inner
class A(object):
def foo(self):
print "foo called"
def bar(self):
print "BAR CALLED"
class B(A):
#wrapper
def foo(self):
super(B, self).foo()
class C(A):
#wrapper
def bar(self):
super(C, self).bar()
Stick = A()
Dave = B()
Jupiter = C()
Jupiter.foo() #prints "foo called"
Jupiter.bar() #prints "bar wrapped" and "BAR CALLED"

Delegating #classmethods in python

I need a delegated class to delegate a #classmethod. Here's what I've tried:
class Foo(object):
def __init__(self, a):
self.a = a
#classmethod
def from_a(cls, a):
return cls(a)
class Bar(object):
def __init__(self, foo):
elf._foo = foo
def __getattribute__(self, name):
return getattr(self._foo, name)
But, of course this doesn't define how to look up attributes of Foo (not of an instance of Foo), so Bar.from_a(5) will raise an AttributeError. While it is of course possible to do this explicitly by defining a from_a method on Bar or to do this at instantiation by calling Bar(Foo.from_a(5)), I would rather do this implicitly. Ideas?
I started working on what I thought would be a simple approach for this using a metaclass, but it is actually fairly complex. What you should probably be doing here is having Bar inherit from Foo, but I'll show you what I came up with all the same:
import types
import functools
def make_delegating_type(delegatee):
class DelegatingType(type):
def __getattr__(self, name):
obj = getattr(delegatee, name)
if isinstance(obj, (types.FunctionType, types.MethodType)):
#functools.wraps(obj)
def wrapper(*args, **kwargs):
result = obj(*args, **kwargs)
if isinstance(result, delegatee):
return self(result)
return result
return wrapper
return obj
return DelegatingType
class Foo(object):
def __init__(self, a): self.a = a
#classmethod
def from_a(cls, a): return cls(a)
class Bar(object):
__metaclass__ = make_delegating_type(Foo)
def __init__(self, foo): self._foo = foo
def __getattr__(self, name): return getattr(self._foo, name)
Note that in 3.x you would use class Bar(object, metaclass=make_delegating_type(Foo) instead of the __metaclass__ = make_delegating_type(Foo) line at the top of the Bar class body.
Here is how this works. Your current version currently delegates attribute lookups on instances of Bar to an instance of Foo, this uses a metaclass so that attributes lookups on the class Bar are delegated to the class Foo as well. Unfortunately it is not as simple as just using a __getattr__ definition that returns getattr(delegatee, name), because if the attribute your a looking up is a factory function as in your example you need a version of that factory function that will return an instance of your delegating type. So for example Bar.from_a(5) should be the same as Bar(Foo.from_a(5)), and with the naive approach you would just get Foo.from_a(5). That is why there is all the logic detecting if the attribute is a function or method, and creating a wrapper that checks the return type of that function/method.
To reiterate, I do not recommend that you use this code! It is much more complicated then just defining from_a on Bar or having Bar inherit from Foo. But hopefully it will be a learning experience for you, as it was for me.

Categories