Static method

M

mk

Hello everyone,

Disclaimer: I'm doing this mainly for learning purposes, to feel what
it's good for.

I'm trying to get print_internal_date become a static method AND to
refer to it in a class attribute 'tagdata' dict.

class PYFileInfo(FileInfo):
'python file properties'

@staticmethod
def print_internal_date(filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': print_internal_date
}

def __init__(self, fname=None):
FileInfo.__init__(self,fname)

def __setitem__(self, key, value):
FileInfo.__setitem__(self, key, value)
if key == 'name' and value:
self.__get_props(value)

def __get_props(self, value):
py_compile.compile(value)
for tag, fun in PYFileInfo.tagdata.items():
self[tag] = fun(value)

But:

c:/Python26/pythonw.exe -u "C:/mp3i/finfo2.py"
Traceback (most recent call last):
File "C:/mp3i/finfo2.py", line 100, in <module>
insts = list_dir(r'c:\mp3i',['.mp3', '.py'])
File "C:/mp3i/finfo2.py", line 93, in list_dir
insts = [c(f) for c,f in class_files]
File "C:/mp3i/finfo2.py", line 69, in __init__
FileInfo.__init__(self,fname)
File "C:/mp3i/finfo2.py", line 12, in __init__
self['name'] = filename
File "C:/mp3i/finfo2.py", line 74, in __setitem__
self.__get_props(value)
File "C:/mp3i/finfo2.py", line 79, in __get_props
self[tag] = fun(value)
TypeError: 'staticmethod' object is not callable


I think I know where the problem is: what resides in tagdata is a static
method 'wrapper', not the function itself, according to:

http://docs.python.org/reference/datamodel.html

"Static method objects
Static method objects provide a way of defeating the transformation
of function objects to method objects described above. A static method
object is a wrapper around any other object, usually a user-defined
method object. When a static method object is retrieved from a class or
a class instance, the object actually returned is the wrapped object,
which is not subject to any further transformation. Static method
objects are not themselves callable, although the objects they wrap
usually are."

So, how do I get out the wrapped function out of static method without
class call or instance call? (to be called in self[tag] = fun(value))

Yes, I do know that if I just get rid of @staticmethod, this works
without a hitch.
 
S

Stefan Behnel

mk, 18.02.2010 12:12:
I'm trying to get print_internal_date become a static method AND to
refer to it in a class attribute 'tagdata' dict.

class PYFileInfo(FileInfo):
'python file properties'

@staticmethod
def print_internal_date(filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': print_internal_date
}

You can 'unroll' the decorator like this:

class PYFileInfo(FileInfo):
def print_internal_date(filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': print_internal_date
}

print_internal_date = staticmethod(print_internal_date)

You can also define the function outside of the class and then assign it to
a class attribute, i.e.

def _print_internal_date(filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

class PYFileInfo(FileInfo):
print_internal_date = _print_internal_date

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': _print_internal_date
}

Quite likely, there are other ways to achieve what you want more cleanly,
but without more background on your intended use case, it's hard to give
better advice.

Stefan
 
B

Bruno Desthuilliers

mk a écrit :
I'm trying to get print_internal_date become a static method AND to
refer to it in a class attribute 'tagdata' dict.

class PYFileInfo(FileInfo):
'python file properties'

@staticmethod
def print_internal_date(filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': print_internal_date
} (snip)

def __get_props(self, value):
py_compile.compile(value)
for tag, fun in PYFileInfo.tagdata.items():
self[tag] = fun(value)

But:

c:/Python26/pythonw.exe -u "C:/mp3i/finfo2.py"
Traceback (most recent call last): (snip)
File "C:/mp3i/finfo2.py", line 79, in __get_props
self[tag] = fun(value)
TypeError: 'staticmethod' object is not callable


I think I know where the problem is: what resides in tagdata is a static
method 'wrapper', not the function itself, according to:

Indeed. Sorry, I'm afraid I gave you bad advice wrt/ using a
staticmethod here - I should know better :( (well, OTHO staticmethods
are not something I use that often).

Anyway: here are a simplified version of your problem, a possible
solution that _won't_ statisfy your other constraints, 2 ugly hacks that
could work but that I don't really like, and what's possibly the "less
worse" solution if you really need a staticmethod here.

###
class Foo1(object):
""" simplified version of mk's code - test() fails """
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': bar}

def test(self, baaz):
self.tagada['bar'](baaz)


class Foo2(object):
""" naive solution : kinda work, BUT will fail
with the real code that has plain functions
in 'tagada'
"""
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': bar}

def test(self, baaz):
self.tagada['bar'].__get__(self)(baaz)


class Foo3(object):
""" working solution 1 : defer the wrapping
of 'bar' as a staticmethod
"""
def bar(baaz):
print baaz

tagada = {'bar': bar}

bar = staticmethod(bar)

def test(self, baaz):
self.tagada['bar'](baaz)


class Foo4(object):
""" working solution 2 : use a lambda """
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': lambda x : Foo4.bar(x)}

def test(self, baaz):
self.tagada['bar'](baaz)


""" and as a "less worse" solution """

def foo5bar(baaz):
print baaz

class Foo5(object):
tagada = {'bar': foo5bar}

bar = staticmethod(foo5bar)

def test(self, baaz):
self.tagada['bar'](baaz)


###

Another "solution" might be to write an alternate callable
implementation of 'staticmethod' - which I'll leave as an exercise to
the reader (...) - but that's possibly a bit overkill !-)

http://docs.python.org/reference/datamodel.html

So, how do I get out the wrapped function out of static method without
class call or instance call? (to be called in self[tag] = fun(value))

cf above. None of the solutions I could came with really statisfy me,
but well, at least 3 of them might be "good enough" depending on the
context. As far as I'm concerned, I'd first try the last one, but YMMV
Yes, I do know that if I just get rid of @staticmethod, this works
without a hitch.

But then you can't use print_internal_date as a method !-)

HTH
 
M

mk

Bruno said:
Indeed. Sorry, I'm afraid I gave you bad advice wrt/ using a
staticmethod here - I should know better :( (well, OTHO staticmethods
are not something I use that often).

Do not worry the least bit! I'm testdriving on a closed circuit here for
sake of becoming better 'driver', I'm not going to drive like this on
the streets.
class Foo2(object):
""" naive solution : kinda work, BUT will fail
with the real code that has plain functions
in 'tagada'
"""
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': bar}

def test(self, baaz):
self.tagada['bar'].__get__(self)(baaz)

Well I could always do:

if isinstance(self.tagada['bar'], staticmethod):
self.tagada['bar'].__get__(self)(baaz)
else:
self.tagada['bar'](baaz)

But 1. this apparently defeats the purpose of using print_internal_date
on instance/class in 'normal' way, and 2. I probably shouldn't be doing
that since using isinstance is apparently like playing with yourself:
while technically legal, people look at you weirdly. :)
class Foo3(object):
""" working solution 1 : defer the wrapping
of 'bar' as a staticmethod
"""
def bar(baaz):
print baaz

tagada = {'bar': bar}

bar = staticmethod(bar)

def test(self, baaz):
self.tagada['bar'](baaz)

Neat! I like this one.
class Foo4(object):
""" working solution 2 : use a lambda """
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': lambda x : Foo4.bar(x)}

def test(self, baaz):
self.tagada['bar'](baaz)

Huh? How does this one work? After all, while in Foo4 body, the Foo4
does not exist yet? Does lambda defer evaluation to runtime (when it's
executed) or smth?
""" and as a "less worse" solution """

def foo5bar(baaz):
print baaz

class Foo5(object):
tagada = {'bar': foo5bar}

bar = staticmethod(foo5bar)

def test(self, baaz):
self.tagada['bar'](baaz)

Yes. I probably should have stayed with this one in the first place. I
feel bad for using up bandwidth and people's attention with such stuff...
 
B

Bruno Desthuilliers

mk a écrit :
Bruno Desthuilliers wrote: (snip)
class Foo2(object):
""" naive solution : kinda work, BUT will fail
with the real code that has plain functions
in 'tagada'
"""
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': bar}

def test(self, baaz):
self.tagada['bar'].__get__(self)(baaz)

Well I could always do:

if isinstance(self.tagada['bar'], staticmethod):
self.tagada['bar'].__get__(self)(baaz)
else:
self.tagada['bar'](baaz)

But 1. this apparently defeats the purpose of using print_internal_date
on instance/class in 'normal' way, and 2. I probably shouldn't be doing
that since using isinstance is apparently like playing with yourself:
while technically legal, people look at you weirdly. :)

As far as I'm concerned, this would be a valid use case for isinstance.
But it breaks uniformity and requires quite a lot of mostly useless code.
class Foo3(object):
""" working solution 1 : defer the wrapping
of 'bar' as a staticmethod
"""
def bar(baaz):
print baaz

tagada = {'bar': bar}

bar = staticmethod(bar)

def test(self, baaz):
self.tagada['bar'](baaz)

Neat! I like this one.

Not me - with the wrapping so far away from the definition, it's too
easy to miss part of the "process" when you come back to this code 6
month later.
class Foo4(object):
""" working solution 2 : use a lambda """
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': lambda x : Foo4.bar(x)}

def test(self, baaz):
self.tagada['bar'](baaz)

Huh? How does this one work? After all, while in Foo4 body, the Foo4
does not exist yet? Does lambda defer evaluation to runtime (when it's
executed) or smth?

or smth, yes !-)

A lambda expression evals to an ordinary function - just like a def
statement - so Foo4 is not resolved until Foo4.tagada['bar'] is actually
called.
""" and as a "less worse" solution """

def foo5bar(baaz):
print baaz

class Foo5(object):
tagada = {'bar': foo5bar}

bar = staticmethod(foo5bar)

def test(self, baaz):
self.tagada['bar'](baaz)

Yes. I probably should have stayed with this one in the first place. I
feel bad for using up bandwidth and people's attention with such stuff...

Why so ? You learned something, I learned something, and quite a few
other people will now have a chance to learn something. Sensible use of
bandwith as far as I'm concerned. wrt/ people's attention, don't worry,
it's up to the reader to pay attention or skip the whole topic !-)
 
M

mk

Bruno said:
class Foo4(object):
""" working solution 2 : use a lambda """
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': lambda x : Foo4.bar(x)}

def test(self, baaz):
self.tagada['bar'](baaz)

Huh? How does this one work? After all, while in Foo4 body, the Foo4
does not exist yet? Does lambda defer evaluation to runtime (when it's
executed) or smth?

or smth, yes !-)

A lambda expression evals to an ordinary function - just like a def
statement - so Foo4 is not resolved until Foo4.tagada['bar'] is actually
called.

This works, but... Foo4.bar in tagada is a staticmethod. So what's
needed is Foo4.bar.__get__(x) (not that I'm that smart, I just got
'staticmethod is not callable' exception).

It appears I have to use __get__ anyway while referring to bar in Foo4
methods:

def testrefer(self, val):
self.bar.__get__(val)
Foo4.bar.__get__(val)

At least I have to do this in my own code:

def testit(self, fname):
self.print_internal_date.__get__(fname + 'c')
PYFileInfo.print_internal_date.__get__(fname + 'c')


Or else I get "TypeError: 'staticmethod' object is not callable".
 
B

Bruno Desthuilliers

mk a écrit :
Bruno said:
class Foo4(object):
""" working solution 2 : use a lambda """
@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': lambda x : Foo4.bar(x)}

def test(self, baaz):
self.tagada['bar'](baaz)

Huh? How does this one work? After all, while in Foo4 body, the Foo4
does not exist yet? Does lambda defer evaluation to runtime (when
it's executed) or smth?

or smth, yes !-)

A lambda expression evals to an ordinary function - just like a def
statement - so Foo4 is not resolved until Foo4.tagada['bar'] is
actually called.

This works, but... Foo4.bar in tagada is a staticmethod. So what's
needed is Foo4.bar.__get__(x) (not that I'm that smart, I just got
'staticmethod is not callable' exception).

Huh ???

class Foo4(object):

@staticmethod
def bar(baaz):
print baaz

tagada = {'bar': lambda x : Foo4.bar(x)}

def test(self, baaz):
self.tagada['bar'](baaz)
WorksForMe(tm).



It appears I have to use __get__ anyway while referring to bar in Foo4
methods:
>
def testrefer(self, val):
self.bar.__get__(val)
Foo4.bar.__get__(val)

At least I have to do this in my own code:

def testit(self, fname):
self.print_internal_date.__get__(fname + 'c')
PYFileInfo.print_internal_date.__get__(fname + 'c')


Or else I get "TypeError: 'staticmethod' object is not callable".

I think you broke something somewhere. Assuming you're using Python 2.x
(>= 2.3 IIRC), my above code works.
 
M

mk

Bruno said:
I think you broke something somewhere. Assuming you're using Python 2.x
(>= 2.3 IIRC), my above code works.


ARGH! Forgot the "delayed staticmethod" line -- in effect I called
staticmethod twice:

@staticmethod
def print_internal_date(filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': lambda x:
PYFileInfo.print_internal_date.__get__(x)
}

# HERE I BROKE IT
print_internal_date = staticmethod(print_internal_date)

def __init__(self, fname=None):
FileInfo.__init__(self,fname)

def __setitem__(self, key, value):
FileInfo.__setitem__(self, key, value)
if key == 'name' and value:
self.__get_props(value)

def testit(self, fname):
self.print_internal_date(fname+'c')
PYFileInfo.print_internal_date.__get__(fname+'c')


You're right. It works.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top