I want to create a kind of utility class which contains only static methods which are callable by the name class prefix. Looks like I'm doing something wrong :)
Here is my small class:
class FileUtility():
#staticmethod
def GetFileSize(self, fullName):
fileSize = os.path.getsize(fullName)
return fileSize
#staticmethod
def GetFilePath(self, fullName):
filePath = os.path.abspath(fullName)
return filePath
Now my "main" method:
from FileUtility import *
def main():
path = 'C:\config_file_list.txt'
dir = FileUtility.GetFilePath(path)
print dir
and I got an error: unbound method GetFilePath() must be called with FileUtility instance as first argument (got str instance instead).
A have a few questions here:
What am I doing wrong? Should not the static method be callable by classname?
Do I really need a utility class, or are there other ways to achieve the same in Python?
If I try to change the code in main I'm getting: TypeError: GetFilePath() takes exactly 1 argument (2 given)
The new main:
from FileUtility import *
def main():
objFile = FileUtility()
path = 'H:\config_file_list.txt'
dir = objFile.GetFilePath(path)
print dir
You're getting the error because you're taking a self argument in each of those functions. They're static, you don't need it.
However, the 'pythonic' way of doing this is not to have a class full of static methods, but to just make them free functions in a module.
#fileutility.py:
def get_file_size(fullName):
fileSize = os.path.getsize(fullName)
return fileSize
def get_file_path(fullName):
filePath = os.path.abspath(fullName)
return filePath
Now, in your other python files (assuming fileutility.py is in the same directory or on the PYTHONPATH)
import fileutility
fileutility.get_file_size("myfile.txt")
fileutility.get_file_path("that.txt")
It doesn't mention static methods specifically, but if you're coming from a different language, PEP 8, the python style guide is a good read and introduction to how python programmers think.
You really shouldn't be creating static methods in Python. What you should be doing is putting them at the global function level, and then accessing the module they're in when you call them.
foo.py:
def bar():
return 42
baz.py:
import foo
print foo.bar()
Static methods don't get the object passed in as the first parameter (no object)
remove the self parameter and the calls should work.
The import problem is relevant too.
And the static comment relevant too.
In python, java-like (or whatever) static methods are not widely used as they don't really have a purpose.
Instead, you should simply define your "methods" as functions in a module:
#module1.py
def fun1():
return do_stuff()
def fun2(arg):
return do_stuff_with_arg(arg)
#main.py
import module1
if __name__ == '__main__':
a = module1.fun()
print module1.fun2(a)
Just remove self in methods definition. Your intention is to use as static. Self is to work with instance of that class.
Just remove the self in the function definition. Since your using the static functions so you need not pass self as an argument for the functions.
So your class and function should be like this:
class FileUtility():
#staticmethod
def GetFileSize(fullName):
fileSize = os.path.getsize(fullName)
return fileSize
#staticmethod
def GetFilePath(fullName):
filePath = os.path.abspath(fullName)
return filePath
If you want to use your functions defined in the class, you have just to create an instance of your class and apply the function.
So the result is :
dir = FileUtility().GetFilePath(path)
Just add () after your class name.
#staticmethod is not needed as you are using standard function, not static. But in your case the result is the same.
Related
I have a Python file from which I would like to get all functions. I want to pass these functions to another file in order to collect data about these functions.
For Example.py:
class Example:
def method_to_extract(name: str) -> none:
print(name)
I want to extract this method as an object through the file name and pass it as an argument to another function, like
func = magic_method(Example.py) # returns method_to_extract as a callable object
collect_data(func)
Is this possible? So far, I have only been able to extract the names of functions as strings. However, that does not help me, as I want to collect data about the functions like the number of arguments, type hints, and more, and therefore need to be able to access the entire function.
I also tried getting the function using func = getattr(class, funcname), but I wasn't able to pass func as a parameter and extract the data I need.
I can think of 3 solutions, dependant on your particular needs.
Simplest one: don't use classes, just global functions:
# file1.py
def method_to_extract(name):
print(name)
And then in another file just import and use it:
# file2.py
from file2 import method_to_extract
method_to_extract()
In case you especially want to use methods inside of a class, you can make them static with #staticmethod decorator:
# file1.py
class Example:
#staticmethod
def method_to_extract(name):
print(name)
And then in another file just import and use it:
# file2.py
from file2 import Example
Example.method_to_extract()
More on staticmetod decorator here: https://www.programiz.com/python-programming/methods/built-in/staticmethod
Of course, not all of your methods can be static. Then you just have to create an instance of the class and then use its methods:
# file1.py
class Example:
def method_to_extract(name):
print(name)
And then in another file just import and use it:
# file2.py
from file2 import Example
instance = Example()
instance.method_to_extract()
Basically, the above three approaches are in general only possible ways of accessing some particular function or method in python and the fact of exporting them to another file doesn't change a thing.
Something like this:
from Example import Example.method_to_extract as mte
collect_data(mte)
The code you show is not entirely correct; you would have to make method_to_extract a #staticmethod if it's inside the Example class. Perhaps a better solution then would be to define it outside the class; or define a #classmethod which you can call to return a method of that class as an object. But both of these are generally best avoided; your class should probably simply have a method which implements collect_data, and your code should simply call that instead.
I am writing a test, where I create a constructor.
This constructor has one parameter, which I pass a Mock inside.
But except for this parameter, there is another function inside of the parameter and I'm not sure how to get the constructor to see it as a Mock.
Here's an easy example:
Class MyClass():
def __init__(self, var):
self._var = var
self._func()
# Other stuff, I actually care about and can easily check **
Now it's easy to handle the var if I pass it as a parameter in the test:
def test_trying_mock(self):
var = MagicMock()
object = MyClass(var)
And the var line is handled. How can I make the constructor see func as mock and make it skip it to the part I actually wanna run and check?
After some research I found out how to do it:
In the testing class, under set_up, I added self._mock_func().
Outside of set_up, I implemented this function like this:
def _mock_func(self):
class_path = ' [path to MyClass] '
self._func_mock = self.mock_object('_func', path= class_path)
Then, when the constructor runs, it will give this function a mock and keep running.
I thought that the following code would result in an error because as far as I have read, a method in a Python class must either have "self" (or any other label, but "self" by convention) as its first argument, or "cls" or similar if the #classmethod decorator is used, or none if the #staticmethod decorator is used.
How come I get no error running this with Python 3.5 in the Terminal, even though test_method does not meet these requirements? It seems to work fine as a static method, but without the decorator.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
class MyClass:
def test_method(args):
print(args[1])
#staticmethod
def static_method():
print("static_method")
#classmethod
def class_method(cls):
print("class_method")
def main(args):
MyClass.test_method(args)
if __name__ == '__main__':
sys.exit(main(sys.argv))
Output:
$ python3 testscript.py "testing"
$ testing
EDIT:
My question could also be phrased differently, drawing attention away from self and to #staticmethod: "How come I'm getting a seemingly working static method without the #staticmethod decorator?"
In Python 2, functions defined in a class body are automatically converted to "unbound methods", and cannot be called directly without a staticmethod decorator. In Python 3, this concept was removed; MyClass.text_method is a simple function that lives inside the MyClass namespace, and can be called directly.
The main reason to still use staticmethod in Python 3 is if you also want to call the method on an instance. If you don't use the decorator, the method will always be passed the instance as the first parameter, causing a TypeError.
There is nothing special about this. In python 3 there is no difference between a function defined inside a class or a function defined outside a class. Both of them are normal functions.
The self that you are talking about here or maybe cls comes into picture only when you access the function through an instance. Hence here you didn't get any error.
However if you modify your code just a little bit to look like the following, then you'd get an error that you expected.
def main(args):
MyClass().test_method(args)
# Should throw an error
EDIT:
#staticmethod will work on both class instances like MyClass().test_method(args)and just a regular direct call like MyClass.test_method(args)
However a regular method(without self in it) can't be called on a class instance. So you will always have to call it as MyClass.test_method(args)
self isn't necessarily required. However, if you want to reference any variable or value that is associated with the object(instantiation of the class) (E.g. for a class about cars, it's speed, self.speed) you'll need to have self as a parameter in the function. For this reason, it's common practice to always have self as an argument, otherwise you aren't really using the class for the right reason.
EDIT:
This will actually throw an error if you do the following:
class a():
def __init__(self, x):
self.asd = x
def hello(x):
print(x)
>>> g = a(4)
>>> g.hello(5)
as when calling "hello", both "self" and "4" will be passed as parameters. It would work in the following instance, which is what I was saying above:
>>> g = a
>>> g.hello(4)
or
>>> a.hello(4)
To add on to the existing answers here and provide a code example:
class MyClass:
def __init__(self):
pass
def myStaticMethod():
print("a static method")
#staticmethod
def myStaticMethodWithArg(my_arg):
print(my_arg)
print("a static method")
MyClass.myStaticMethod()
MyClass.myStaticMethodWithArg("skhsdkj")
abc = MyClass()
abc.myStaticMethodWithArg("avc")
Try removing the #staticmethod decorator and rerunning the code and see what happens! (The very last call will fail since the method is passed in both self and the string input. By adding the decorator, we can guide the interpreter to perform our desired action)
I have two modules called Dfs and Graph.
In my Graph module, I have a class Graph and a method called ReadGraph.
In my Dfs module, I have a method that calls on ReadGraph but I get the following error message when I type: Dfs.ProcessGraph(testcase.txt,verbose=True)
Error message:
NameError: name 'testcase' is not defined
Could someone explain how to fix this?
Thanks.
From my Dfs.py module:
import sys
from Graph import *
class Dfs( object ):
def ProcessGraph(file_name, verbose):
g=ReadGraph(file_name)
From my Graph.py module:
class Graph( object ):
def ReadGraph( file_name ):
Remove your class declaration from Graph.py. When you import all from a file you get all top level objects. In this case it's the Graph class itself, not its methods.
Also you need to pass string 'testcase.txt' not testcase.txt.
You have multiple problems here:
If you from Graph import * (which is bad practice to start with), you bring Graph into your name space. However, ReadGraph is inside Graph, so to access it you need Graph.ReadGraph.
Having done that, you try to call Dfs.ProcessGraph(testcase.txt,verbose=True). The first argument is interpreted as "pass the txt attribute of the object referenced by name testcase, which doesn't exist. Instead, you mean "testcase.txt" (quoted to make it a string).
Having done all of that, you get e.g. TypeError: unbound method ProcessGraph() must be called with Dfs instance as first argument (got str instance instead). When you call an instance method, the first argument, self by convention, is the instance itself. You have two choices; either a) make e.g. ProcessGraph a #staticmethod, and access it Graph.ReadGraph; or b) move it outside the class, then you can access it directly like you tried to in the first place. As you don't seem to have any class or instance attributes, it's not clear why you are bothering with the classes at all.
What it should probably look like:
import sys
from Graph import read_graph
def process_graph(file_name, verbose):
g = read_graph(file_name)
Graph.py module (note absence of class Graph):
def read_graph(file_name):
...
(Generally, I suggest you read PEP 8).
your code should be : Dfs.ProcessGraph('testcase.txt',verbose=True)
and not Dfs.ProcessGraph(testcase.txt,verbose=True)
'testcase.txt' # is a string and should be between quotes
also check if it is in the same directory where your code live else point to it
plus in DFs you should instantiate Graph :
from Graph.Graph import *
g = Graph()
grf = g.ReadGraph('filename')
EDIT: to be more precise
in Graph module:
class Graph(object):
def __init__(self):
pass # for the example now
def read_graph(self, file_name):
return file_name
in Dfs Module:
from Graph import *
class Dfs(object):
def __init__(self):
pass # for the example now
def ProcessGraph(file_name, verbose):
g = Graph()
file_name = Graph.read_graph(file_name)
I am trying to learn about classes, can someone explain to me why this code is not working. I thought when calling a function from a class, "self" is automatically ommitted, but the interpreter tells me that argument "a" is missing (he thinks self = 10).
#! coding=utf-8
class test:
def __init__(self):
"do something here"
def do(self,a):
return a**2
d = test.do
print(d(10))
Instantiate the class first:
d = test().do
print(d(10)) # prints 100
test.do is an unbound method, test().do is bound. The difference is explained in this thread: Class method differences in Python: bound, unbound and static.
You have to instantiate the class first:
d = test()
then you can call a method:
print(d.do(10))
if you want to use method statically you have to declare it in python
#! coding=utf-8
class test:
def __init__(self):
"do something here"
#staticmethod
def do(a):
return a**2
d = test.do
print(d(10)) #and that's work
Since you haven't instantiated the class (a fancy term for created) you can't be assigning methods to any random variable. Like already said, you must create the object first, whilst making sure the method you call is a part of the class you called or connected to the class in some way (such as creating another class and then communicating that class with the current class). So you should first type d=test() followed by d.do().
Also, remember that in your declaration of the method you crated a parameter so what you done was wrong in itself anyway, because when you declared the do function, you should have put within the brackets the number you wanted to send to the method to calculate its square. So you type test.do(10) and then the 10 is sent by the self reference to the method to be done whatever it is you told it to do.
One more thing: although it isn't a huge deal, it helps if all of your class names begin with a capital letter, as this is usually the 'pythonic' way to do things, and it also makes your code much easier to read, because when you first called the class, somebody could easily mistaken it for an ordinary function
class test:
def __init__(self):
"do something here"
def do(self,a):
return a**2
def __call__(self,a):
return self.do(a)
a = test
test.do(a,10)
#or
a = test().do
a(10)
#or
a = test()
test.do(a,10)
#or
a = test()
print(a(10))