[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

Static method

mk

2/18/2010 11:13:00 AM

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/data...

"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.



7 Answers

Stefan Behnel

2/18/2010 11:33:00 AM

0

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

Bruno Desthuilliers

2/18/2010 11:55:00 AM

0

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/data...
>
> 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

mk

2/18/2010 12:43:00 PM

0

Bruno Desthuilliers wrote:
>> 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).

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...


Bruno Desthuilliers

2/18/2010 1:00:00 PM

0

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 !-)



mk

2/18/2010 1:41:00 PM

0

Bruno Desthuilliers wrote:

>>>
>>>
>>> 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".





Bruno Desthuilliers

2/18/2010 2:23:00 PM

0

mk a écrit :
> Bruno Desthuilliers wrote:
>
>>>>
>>>>
>>>> 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)

>>> f = Foo4()
>>> f.test(42)
42
>>> Foo4.bar(42)
42
>>>

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.

mk

2/18/2010 3:44:00 PM

0

Bruno Desthuilliers wrote:

> 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.