calling class/static method from class variable in python - python

I'm trying to make a ImageLoader class handle the loading and processing of image resources like this:
class ImageLoader:
TileTable = __loadTileTable('image path', some other variable)
#staticmethod
def _loadTileTable(arg1, arg2):
blah blah
however, on compile i get: NameError: name '_loadTileTable' is not defined
If i replace the second line with TileTable = ImageLoader.__loadTileTable('image path', some other variable) then i get NameError: name 'ImageLoader' is not defined
As i'm going from C# to Python, static classes with static methods is what i'd use to implement this. However, i'm open to how I'd do this in general in python (that is, call static library functions that are only grouped together by their functionality).
UPDATE:
After reading both answers, I'm getting a picture that what i'm trying to do probably isn't right.
How would I go about imlementing ImageLoader so that I can do this:
Assuming that tile table returned an array
module1.py
aTile = ImageLoader.TileTable[1]
module2.py
anotherTile = ImageLoader.TileTable[2]
ideally, i'd populate TileTable just once.
Update:
Thanks for all the answers, I found my last answer to populating TileTable just once in the python modules doco
"A module can contain executable
statements as well as function
definitions. These statements are
intended to initialize the module.
They are executed only the first time
the module is imported somewhere"
As for static class, i'm going to forgo classes and just make a module level variable.

Answering just the updated question, what you would do in Python is make TileTable a variable called tile_table in a module called imageloader. There is no reason at all to put any of this inside a class.
So then you get:
module1.py
import imageloader
aTile = imageloader.tile_table[1]
module2.py
import imageloader
anotherTile = imageloader.tile_table[2]
and imageload.py looks something like:
def _loadTileTable(arg1, arg2):
pass # blah blah
tile_table = _loadTileTable('image path', other_var)
Think of a Python module as a singleton instance in other languages (which in fact it is) and you'll be able to reconcile this with any OO preconceptions you inherited from other languages.

In Python, the code in the class block is first executed, then the resultant namespace is passed to the class initializer. The code you wrote could have also been written as:
TileTable = _loadTileTable(arg1, arg2)
#staticmethod
def _loadTileTable(arg1, arg2):
pass # blah blah
ImageLoader = type('ImageLoader', (), {'TileTable': TileTable, '_loadTileTable': _loadTileTable})
del TileTable
del _loadTileTable
As you can see, the call of _loadTileTable appears before the definition of it. In your example, within the class definition, the call to _loadTileTable must come after the definition of _loadTileTable.
One possible fix is to simply re-arrange the class definition.
class ImageLoader:
def _loadTileTable(arg1, arg2):
pass # blah, blah
TileTable = _loadTileTable('image path', other_var)
Note that I removed the 'staticmethod', because at the point where _loadTileTable is called, it's being called as a function and not a method. If you really want it to be available after class initialization, you can define it as a static method after the fact.
class ImageLoader:
def _loadTileTable(arg1, arg2):
pass # blah, blah
TileTable = _loadTileTable('image path', other_var)
_loadTileTable = staticmethod(_loadTileTable)

Class-level variables which get updated are a bad, bad thing. Our default expectation is that object instances are stateful and classes are stateless.
In this case, we're trying to "magically" initialize a collection as a class variable, which is a toweringly bad idea. A collection is simply an object with simple instance-level attributes.
The magical Tile Table should not be a concealed, static part of the ImageLoader. There is no possible reason for that. It should be an argument to the ImageLoader if you want to avoid loading it more than once.
Separating these promotes testability. It's not arbitrary. It's how unit testing gets done.
What you want is this.
class ImageLoader( object ):
def __init__( self, theTileTable ):
self.tile_table= theTileTable
class TileTable( object ):
def __init__( self, path, some_other_arg ):
self.tileTable= self._loadTileTable( path, some_other_arg )
def _loadTileTable(arg1, arg2):
blah blah
No static anything. Independent units. More easily testable. No weird dependencies. No magic.

Is there a design reason you're using a static method? If so, because you're not overloading the class initialization, you'll need to declare the variable after the method definition.
But, if you do this, you'lll get a new error:
NameError: name 'arg1' is not defined
The reason for this is because you're executing the method within the class before the class is even instantiated, therefore you never have a chance to pass the arguments to the method.
So, the proper way to do this is to overload the __init__() method so that assignment to TileTable only happens when the class is constructed:
class ImageLoader(object):
def __init__(self, arg1, arg2):
self.TileTable = self._loadTileTable(arg1, arg2)
#staticmethod
def _loadTileTable(arg1, arg2):
print arg1, arg2
This gives you the ability to call ImageLoader._loadTileTable() without having an instance, but then it also allows you to create the TileTable instance variable upon creating an instance.
Using a Class method
In response to my comment about the possible need for a classmethod, here is an example that covers this:
class ImageLoader:
#classmethod
def _loadTileTable(cls, arg1, arg2):
return arg1, arg2
# We're creating the class variable outside of the class definition. If you're doing
# this in a module, no one will ever notice.
ImageLoader.TileTable = ImageLoader._loadTileTable('foo', 'bar')
There might be a better way to do this, I don't know. But I do think that this covers what you are looking for:
>>> i = ImageLoader()
>>> i
<__main__.ImageLoader instance at 0x100488f80>
>>> ImageLoader.TileTable
('foo', 'bar')
>>> i.TileTable
('foo', 'bar')
There you have an instance i that has access to the class variable, but ImageLoader.TileTable is still available from the class object without the need for an instance.

Related

Python staticmethod decorator with inheritance

My case:
class BaseClass:
#staticmethod
def dummy_decorator(fnc):
def wrapper():
print('Im so dummy')
return wrapper
class InheritedClass(BaseClass):
def __init__(self):
pass
def anymethod(self):
print('hello world')
When I look at dir(), I see my staticmethod
>>> c = InheritedClass()
>>> dir(c)
['__doc__', '__init__', '__module__', 'anymethod', 'dummy_decorator']
Also, I can use my dummy operator as simple staticmethod inside new class.
But when I try to use it as decorator -- I get error
class InheritedClass(BaseClass):
def __init__(self):
pass
#dummy_decorator
def anymethod(self):
print('hello world')
>>> NameError: name 'dummy_decorator' is not defined
Why it works so?
I know, that if I change #dummy_decorator to #BaseClass.dummy_decorator -- everything will work, but why I can't use decorator without ref to parent class?
The reason why is baecause it is a static method, it belongs to the class as you have figured out when you put #BaseClass.dummy_decorator and it worked.
It is an attribute of the class so you can't just refer to it by dummy_decorator unless you move it out of the class or save it into the global namespace
To understand this properly, you need to understand how class definitions work. In a nutshell, everything inside a class block is executed just like regular Python code. Every name that has been created inside that class block (e.g. def or variable assignments) are then wrapped up at the end of the class block and become attributes of the new class object. It goes something like:
# START CAPTURE
def __init__(self):
pass
foo = 'bar'
# END CAPTURE
InheritedClass = # OBJECT WITH ATTRIBUTES "CAPTURED" ABOVE AND ADDITIONAL MAGIC
So, any code within the class block is just regular Python code. It hasn't "inherited" anything yet. That's the "additional magic" applied to the resulting class object at the end of the class block. And since there's no global "dummy_decorator" name defined, you can't call it by that name. It exists as "BaseClass.dummy_decoator", same as it would outside any class block.

Can you use a static method as default parameter in __init__ in python classes?

I am writing a class for a neural network and I want to give it some form of customization, so that you can choose different cost functions and regularizations. For this I want to set them as default parameters in the __init__() method.
But when I pass MyClass.static_method in my example, the Interpreter then tells me that MyClass is not (yet) defined. Why is this and is there a nicer workaround than mine?
You can of course just set the static method as a default parameter, but then other problems arise. For example, if I want to access the functions name (which I actually want), I cannot use __name__ rightaway. I know how to do it another way, by accessing static_method.__func__.__name__. But this seems clumsy and as you get a staticmethod object, seems like its not intended to be used this way.
class MyClass:
#staticmethod
def static_method():
do_something()
def __init__(self, func=MyClass.static_method, func2=static_method):
self.name = func.__name__ #Does not work
self.name2 = func2.__func__.__name__ #Should work
I did expect for the MyClass.static_method to work, but the class does not seem to exist then. So, one last time, why?
The reason you're having problems with your static method usage as a default argument is due to a combination of two issues.
The first issue is that the default argument needs to be well defined when the def statement is run, not only when the function is called. That's because the default argument gets built into the function object, rather than being recalculated each time the function runs (this is the same reason why a mutable default argument like an empty list is often an error). Anyway, this is why you can't use MyClass.static_method as the default argument, since MyClass isn't defined yet when the function is being defined (the class object is only made after all its contents have been created).
The next issue is that a staticmethod object doesn't have all the same attributes and methods as a regular function. Normally this doesn't matter, as when you access it through a class object (e.g. MyClass.static_method once MyClass exists) or through an instance (e.g. self.static_method), it will be callable and have a __name__. But that's because you get the underlying function in those situations, rather than the staticmethod object itself. The staticmethod object itself is a descriptor, but not a callable.
So neither of these functions will work correctly:
class MyClass:
#staticmethod
def static_method():
pass
def foo(self, func=MyClass.static_method): # won't work because MyClass doesn't exist yet
pass
def bar(self, func=static_method): # this declaration will work (if you comment out foo)
name = func.__name__ # but this doesn't work when the bar() is called
func() # nor this, as func is the staticmethod object
What does work would be to use the actual function underlying the staticmethod object as the default:
def baz(self, func=static_method.__func__): # this works!
name = func.__name__
func()
This also works when you pass in some other function (or bound method), unlike the version of your code that used name = func.__func__.__name__.
DEFAULT = object()
class MyClass:
#staticmethod
def static_method():
do_something()
def __init__(self, func=DEFAULT, func2=DEFAULT):
self.name = self.static_method.__name__ if func is DEFAULT else func.__name__
self.name2 = self.static_method.__func__.__name__ if func2 is DEFAULT else func2.__func__.__name__
I guess??

How to use self parameter, #staticmethod keyword inside a class and its methods

I have a python class which has multiple methods. I have defined my methods via #staticmethod instance and I want to call other methods of my class from inside my main function(main_function). I think I need self parameter for calling my other functions from my main function and I want to pass this parameter to my main_function when I create an instance of my class.
class myclass:
#staticmethod
def function1(param1)
print "function1"
#staticmethod
def main_function(self, param1)
function1(param1)
my_object = myclass()
my_object.main_function(param1)
I got this error:
TypeError: main_function() takes exactly 2 arguments (1 given)
The problem is that I have not self parameter when I create my instance. I tried to remove #staticmethod keyword from my method definition and remove all self parameter using, but this does not work.
Only use #staticmethod if you are creating a function that you'd normally want to tie to specific classes but do not need any other context. For example, the str.maketrans() function is a static method because it is a utility function you'd often use when working with strings, namespacing it to the already-existing str type (which pre-exists as a class) makes sense there.
You appear to be using classes as a namespace instead. Don't do that. Use a module for your functions, and you don't have to worry about the special scoping rules that apply to classes. Only use a class when you need to bundle state with functionality.
If you insist on using classes with static methods anyway, you are stuck with hardcoding the class name everywhere:
class myclass:
#staticmethod
def function1(param1)
print "function1"
#staticmethod
def main_function(param1)
# Want to use other functions in this class? Then you will
# have to use the full name of the class as a prefix:
myclass.function1(param1)
You could make use of classmethods instead so you have a reference to the class object:
class myclass:
#staticmethod
def function1(param1)
print "function1"
#classmethod
def main_function(cls, param1)
# Now you can use the `cls` reference to access other attributes
cls.function1(param1)
This has the added advantage that you can use inheritance.
However, using a module is the correct way to organise a set of functions into a namespace. Put everything into a my_module.py file in your package, and use importing;
import my_module
my_module.main_function(param1)
Now all globals in my_module are bundled into one module object, and no prefixing or cls references are needed.

Why is `self` not used in this method?

I was under the impression that methods within Python classes always require the self argument (I know that it doesn't actually have to be self, just some keyword). But, this class that I wrote doesn't require it:
import ZipFile
import os
class Zipper:
def make_archive(dir_to_zip):
zf = zipfile.ZipFile(dir_to_zip + '.zip', 'w')
for filename in files:
zf.write(os.path.join(dirname, filename))
zf.close()
See? No self. When I include a self argument to make_archive, I get a TypeError: make_archive() missing one positional argument error. In my search to figure out why this is happening, I actually copied and tried to run a similar program from the docs:
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
print(MyClass.f()) # I added this statement to have a call line
and I get the same error!
TypeError: f() missing 1 required positional argument: 'self'
In the same module that contains the Zipper() class, I have multiple classes that all make use of self. I don't understand the theory here, which makes it difficult to know when to do what, especially since a program copied directly from the docs (this is the docs page) failed when I ran it. I'm using Python 3.5 and 3.4 on Debian Linux. The only thing that I can think of is that it's a static method (and the Zipper.make_archive() as written above works fine if you include #staticmethod above the make_archive method), but I can't find a good explanation to be sure.
You are trying to use it as a static method. In your example;
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
a = MyClass()
a.f() # This should work.
Calling MyClass.f() assumes f is static for MyClass. You can make it static as:
class MyClass:
#staticmethod
def f(): # No self here
return 'hello world'
MyClass.f()
The thing with self is that it's added implicitly. That is, the calling code says Myclass().f(), but the callee sees Myclass().f(self). It also implies that the method is called from some instance of Myclass, which is placed in self variable. The point is that methods are probably somehow using and/or modifying instance data (otherwise why would they be in that class?) and it's handy to have the instance in question automatically supplied.
If you don't need the instance data, you should use either #staticmethod if it's actually more like a function than object method or #classmethod if the method is meant to be inherited and possibly used differently by different classes. See #pankaj-daga answer for a little intro to staticmethods.
The Foo.bar() syntax is also used by functions imported via import Foo instead of from Foo import bar, which is also a possible source of confusion. That, for your purposes, is an entirely different thing.

Python class not in globals when using decorator

Maybe the title is a bit misleading, however I wanted to create a simple decorator to decorate some class methods as "allowed" in an RPC mechanism, but I'm stuck on a strange error when trying to access class variables (Python 2.7.5). Check the code below:
class myclass():
rpcallowedmethods = []
def __init__(self):
pass
def rpcenabled(fn):
print fn
print globals()
print myclass
#rpcenabled
def somefunc(self,param):
pass
c = myclass()
Exception: NameError: global name 'myclass' is not defined
Anyone can explain the reason behind this to me?
EDIT:
What I'm asking is more about the fact that python executes the decorator defined in a class and run against decorated classmethods even prior having the class in the globals, so I believed it's more of a logical "bug" in the python implementation than a seemingly obvious NameError
The actual class object is only assigned to its name after its definition is finished. Thus you cannot use the class name during its definition. You can either create a decorator outside of the class to which you explicitly pass the list you want to fill, or use the following:
class myclass():
rpcmethods = []
def _rpcallowed(fct, l=rpcmethods):
l.append(fct)
return fct
#_rpcallowed
def myfct(): pass
Note that the default parameter (l=rpcmethods) is a workaround as you cannot access a class variable inside of a function without a reference to the class or an instance.
The variant with the decorator outside of the class would probably qualify as being "cleaner" than this as it's explicit and reusable, but it would be a bit more code and less specific.
You're abusing decorators. A decorator is meant to add something to thing object is given. "decorating" it somehow.
The more usual way to do something like this would be to decorate both the method and the class. Metaclasses are another way to solve this problem. They're more powerful, but are overkill for your current problem. However, directly decorating the functions might be all you need to do. And save collating the rpc functions for when a proxy is made.
from types import FunctionType
def enable_rpc(func):
func.rpc_enabled = True
return func
def rpc_enabled_class(cls):
functions = [attr for attr in vars(cls).values()
if isinstance(attr, FunctionType)]
cls._rpc_enabled_methods = [
func for func in functions
if getattr(func, "rpc_enabled", False)
]
return cls
#rpc_enabled_class
class SampleClass(object):
#enable_rpc
def my_func(self):
pass
print(SampleClass._rpc_enabled_methods)
Strange error?
print myclass
caused the error. You can't use the name myclass in its definition...

Categories

Resources