views:

245

answers:

6

I am working through trying to learn to program in Python and am focused on getting a better handle on how to use Standard and other modules. The dir function seems really powerful in the interpreter but I wonder if I am missing something because of my lack of OOP background. Using S.Lotts book I decided to use his Die class to learn more about syntax and use of classes and instances.

Here is the original code:

class Die(object):
''' simulate a six-sided die '''
def roll(self):
 self.value=random.randrange(1,7)
 return self.value
def getValue(self):
 return self.value

I was looking at that and after creating some instances I wondered if the word value was a keyword somehow and what the use of the word object in the class statement did and so I decided to find out by changing the class definition to the following:

class Die():
''' simulate a six-sided die '''
def roll(self):
 self.ban=random.randrange(1,7)
 return self.ban
def getValue(self):
 return self.ban

That change showed me that I got the same behavior from my instances but the following methods/attributes were missing from the instances when I did dir:

'__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
 '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
_repr__', '__setattr__', '__str__', '__weakref__'

I also figured out that when I did a dir on an instance I had an additional keyword-ban which I finally figured out was an attribute of my instance. This helped me understand that I could use d1.ban to access the value of my instance. The only reason I could figure out that this was an attribute was I typed d1.happy and got an AttributeError I figured out that d1.GetValue was a method attached to Die because that is what the interpreter told me.

So when I am trying to use some complicated but helpful module like BeautifulSoup how can I know which of the things that are listed are attributes of my instance or methods of my instance after typing dir(instance). I would need to know this because this poking around has taught me that with attributes I am calling the result of a method and with methods I am invoking a function on my instance.

This question is probably too wordy but it sure did help me better understand the difference between attributes and methods. Specifically, when I look at the result of calling dir on an instance of my Die class I see this

['__doc__', '__module__', 'ban', 'getValue', 'roll']

So it would seem useful to know by looking at that list which are attributes and which are methods without having to resort to trial and error or result to typing in hasattr(myInstance,suspectedAttributeName).

After posting the question I tried

for each in dir(d1):
    print hasattr(d1,each)

which tells me strictly speaking that all methods are attributes. but I can't call a method without the () so it seems to me that the hasattr() is misleading.

+2  A: 

which tells me strictly speaking that all methods are attributes. but I can't call a method without the () so it seems to me that the hasattr() is misleading.

Why is it misleading? If obj.ban() is a method, then obj.ban is the corresponding attribute. You can have code like this:

print obj.getValue()

or

get = obj.getValue
print get()

If you want to get a list of methods on an object, you can try this function. It's not perfect, since it will also trigger for callable attributes that aren't methods, but for 99% of cases should be good enough:

def methods(obj):
    attrs = (getattr(obj, n) for n in dir(obj))
    return [a for a in attrs if a.hasattr("__call__")]
John Millikin
well <b>d1=Die()</b> and then when I type <b>d1.roll</b> I get <b><bound method Die.roll of <__main__.Die instance at 0x089C4A80>></b> so I know that d1.roll!=d1.roll() which suggests to me that methods and attributes are different. But thanks for your answer it helps
PyNEwbie
Methods and attributes are not different. d1.roll is an attribute. I suggest that you try reading an introduction to Python, such as http://www.diveintopython.org/ or http://docs.python.org/tutorial/ , and learn about functions. That might help you understand how methods work.
John Millikin
`obj.ban()` is not a method, it is a method call. `obj.ban` is not an attribute, it is a bound method. `hasattr` is not an attribute of `a`, it is a builtin function.
J.F. Sebastian
Actually, `obj.ban` is an attribute. All bound methods are attributes, else they could not be accessed through dot notation.
John Millikin
@John Millikin I appreciate you taking the time to further explain. so I am using the wrong language. But since when I type d1.ban() in IDLE and I get a type error Also if I type d1.roll I get a message but the value of d1 (I guess ban) does not change I don't understand why you are saying that Methods and attributes are not different. They look different through the interface
PyNEwbie
But, PyNEwbie, as you have the source code I think we're at a loss to understand the confusion. The fact that you can type "d1.roll" and see "<bound method... yadda>" further demystifies any potential for ambiguity when introspecting python apps. Many languages differentiate between a pointer to a function (or, at least, the memory-resident definition of the function) and calling the function. Python is no different in that sense, if it helps to think of it that way.
Jarret Hardie
My earlier comment shouldn't, however, be interpreted a saying you haven't asked a good question, PyNEwbie; you are exploring some very fundamental Python concepts, which is important. dir() is designed to list attributes within a certain namespace... it isn't designed to tell you anything more about those attributes (like callability or signature). Use help() for that, or some_obj.some_attr.__doc__. Otherwise, trying calling it in the interpreter and seeing what happens: python's interpreter encourages such exploration as you've discovered :-)
Jarret Hardie
Jarret-This discussion has been helpful and what I have learned from your amplification is that there is really no shortcut. I have been typing obj.[item from the dir listing of the object all afternoon trying to better understand how to use this majic. I also have the source for BeuatifulSoup open in another window and am slowly but surely getting my head around how to be more efficient. Python is my introduction to this type of language.
PyNEwbie
John so as I am getting closer to an understanding. From your example if obj.ban() is a method; obj.ban is an attribute of that method. My question I think was different. In my example isn't d1 the object. d1 has a method [roll] and when that method has been invoked (called might be better syntax) d1 has an attribute [ban]. Since I am programming the object I want the methods I can apply to the object and the attributes that are applied to the object after the method has been called. Whew! If I say d1.roll I am getting the attribute of the method which is different than the method?
PyNEwbie
+1  A: 

Ideally, when using a complicated library like BeautifulSoup, you should consult its documentation to see what methods each class provides. However, in the rare case where you don't have easily accessible documentation, you can check for the presence of methods using the following.

All methods, which themselves are objects, implement the __call__ method and can be checked using the callable() method which returns True, if the value being checked has the __call__ method.

The following code should work.

x = Die()
x.roll()

for attribute in dir(x) :
    print attribute, callable(getattr(x, attribute))

The above code would return true for all the methods and false for all non callable attributes (such as data members like ban). However, this method also returns True for any callable objects (like inner classes). you can also check if the type of the attribute is instancemethod

JSN
Thanks what you are telling me is that without the documentation or some programming I cannot tell the difference when looking at the dir(obj) that is helpful
PyNEwbie
+1  A: 

There is a built in method called callable. You can apply it to any object and it will return True/False depending on if it can be called. e.g.

>>> def foo():
...   print "This is the only function now"
...
>>> localDictionary = dir()
>>> for item in localDictionary:
...   print repr(item) + "is callable: " + str(callable(locals()[item]))
'__builtins__'is callable: False
'__doc__'is callable: False
'__name__'is callable: False
'foo'is callable: True

Note the locals() call will return a dictionary containing everything defined in your current scope. I did this because the items out of the dictionary are just strings, and we need to run callable on the actual object.

Aldur
callable is deprecated, and has been removed in 3.0. It's better to check for __call__ directly.
John Millikin
+6  A: 

Instead of: "print hasattr(d1,each)", try: "print each, type(getattr(d1,each))". You should find the results informative.

Also, in place of dir() try help(), which I think you're really looking for.

Don
Thanks this does help quite a bit
PyNEwbie
+3  A: 

Consider using the standard library's inspect module -- it's often the handiest approach to introspection, packaging up substantial chunks of functionality (you could implement that from scratch, but reusing well-tested, well-designed code is a good thing). See http://docs.python.org/library/inspect.html for all the details, but for example inspect.getmembers(foo, inspect.ismethod) is an excellent way to get all methods of foo (you'll get (name, value) pairs sorted by name).

Alex Martelli
Thats a neat module-thanks.
PyNEwbie
+2  A: 

This info module inspired from Dive into Python serves the purpose.

def info(obj, spacing=20, collapse=1, variables=False):
    '''Print methods and their doc Strings

    Takes any object'''
    if variables:
 methodList = [method for method in dir(obj)]
    else:
 methodList = [method for method in dir(obj) if callable(getattr(obj,method))]

    #print methodList


    print '\n'.join(['%s %s' %
      (method.ljust(spacing),
       " ".join(str(getattr(obj,method).__doc__).split()))
      for method in methodList])


if __name__=='__main__':
    info(list)
Lakshman Prasad
Wow this is also very helpful-thanks for taking the time to share this.
PyNEwbie