Checking If a Method Is Defined on the Class

checking if a method is defined on the class

Use this:

C.instance_methods(false).include?(:a)
C.instance_methods(false).include?(:b)
C.instance_methods(false).include?(:c)

The method instance_methods return an Array of methods that an instance of this class would have. Passing false as first parameter returns only methods of this class, not methods of super classes.

So C.instance_methods(false) returns the list of methods defined by C.

Then you just have to check if that method is in the returned Array (this is what the include? calls do).

See docs

Check if a method is defined in the base or derived class

You don't need any fancy inspection to do this:

>>> x = child_class_2()                                                     
>>> x.test
<function base_class.test at 0x7fea1a07f7b8>

>>> y = child_class()
>>> y.test
<function child_class.test at 0x7fea1a07f950>

The printed name comes from the __qualname__ attribute of the function by default:

>>> x.test.__qualname__
base_class.test
>>> y.test.__qualname__
child_class.test

A hacky way to get the name of the class is

x.test.__qualname__[:-len(x.test.__name__) - 1]

What is the fastest way to check if a class has a function defined?

Yes, use getattr() to get the attribute, and callable() to verify it is a method:

invert_op = getattr(self, "invert_op", None)
if callable(invert_op):
invert_op(self.path.parent_op)

Note that getattr() normally throws exception when the attribute doesn't exist. However, if you specify a default value (None, in this case), it will return that instead.

Check if method defined on mixee class?

I come with another answer to your post, now using prepend instead of include (only for ruby 2.x). The first is including at the top of the ancestry chain, since the second goes after the class.

Let see how it works at the irb console:

~/rails/learn/ruby (main) > module A
~/rails/learn/ruby (main) | def cancel!
~/rails/learn/ruby (main) | puts 'cancel from m:A'
~/rails/learn/ruby (main) | super if defined? super
~/rails/learn/ruby (main) | end
~/rails/learn/ruby (main) | end
=> :cancel!
~/rails/learn/ruby (main) > class C
~/rails/learn/ruby (main) | prepend A
~/rails/learn/ruby (main) | def cancel!
~/rails/learn/ruby (main) | puts 'cancel from c:C'
~/rails/learn/ruby (main) | end
~/rails/learn/ruby (main) | end
=> :cancel!
~/rails/learn/ruby (main) > C.ancestors
=> [
[0] #<Class:0x000000035421f0>::A,
[1] #<Class:0x000000035421f0>::C < Object,
[2] Object < BasicObject,
[3] PP::ObjectMixin,
[4] Kernel,
[5] BasicObject
]
~/rails/learn/ruby (main) > C.new.cancel!
cancel from m:A
cancel from c:C
=> nil
~/rails/learn/ruby (main) > class D
~/rails/learn/ruby (main) | include A
~/rails/learn/ruby (main) | def cancel!
~/rails/learn/ruby (main) | puts 'cancel from c:D'
~/rails/learn/ruby (main) | super if defined? super
~/rails/learn/ruby (main) | end
~/rails/learn/ruby (main) | end
=> :cancel!
~/rails/learn/ruby (main) > D.ancestors
=> [
[0] #<Class:0x000000035421f0>::D < Object,
[1] #<Class:0x000000035421f0>::A,
[2] Object < BasicObject,
[3] PP::ObjectMixin,
[4] Kernel,
[5] BasicObject
]
~/rails/learn/ruby (main) > D.new.cancel!
cancel from c:D
cancel from m:A
=> nil

I hope it helps with your issue.

Find class in which a method is defined

If you need this in Python 3.x, please see my other answer—the closure cell __class__ is all you need.


If you need to do this in CPython 2.6-2.7, RickyA's answer is close, but it doesn't work, because it relies on the fact that this method is not overriding any other method of the same name. Try adding a Foo.do_it method in his answer, and it will print out Foo, not SomeClass

The way to solve that is to find the method whose code object is identical to the current frame's code object:

def do_it(self):
mro = inspect.getmro(self.__class__)
method_code = inspect.currentframe().f_code
method_name = method_code.co_name
for base in reversed(mro):
try:
if getattr(base, method_name).func_code is method_code:
print(base.__name__)
break
except AttributeError:
pass

(Note that the AttributeError could be raised either by base not having something named do_it, or by base having something named do_it that isn't a function, and therefore doesn't have a func_code. But we don't care which; either way, base is not the match we're looking for.)

This may work in other Python 2.6+ implementations. Python does not require frame objects to exist, and if they don't, inspect.currentframe() will return None. And I'm pretty sure it doesn't require code objects to exist either, which means func_code could be None.

Meanwhile, if you want to use this in both 2.7+ and 3.0+, change that func_code to __code__, but that will break compatibility with earlier 2.x.


If you need CPython 2.5 or earlier, you can just replace the inpsect calls with the implementation-specific CPython attributes:

def do_it(self):
mro = self.__class__.mro()
method_code = sys._getframe().f_code
method_name = method_code.co_name
for base in reversed(mro):
try:
if getattr(base, method_name).func_code is method_code:
print(base.__name__)
break
except AttributeError:
pass

Note that this use of mro() will not work on classic classes; if you really want to handle those (which you really shouldn't want to…), you'll have to write your own mro function that just walks the hierarchy old-school… or just copy it from the 2.6 inspect source.

This will only work in Python 2.x implementations that bend over backward to be CPython-compatible… but that includes at least PyPy. inspect should be more portable, but then if an implementation is going to define frame and code objects with the same attributes as CPython's so it can support all of inspect, there's not much good reason not to make them attributes and provide sys._getframe in the first place…

How can I test if a method is defined in a Perl 6 class?

You should not be comparing types by name.

my \Foo = anon class Foo {}
my \Bar = anon class Foo {}

say Foo.^name eq Bar.^name; # True
say Foo eqv Bar; # False

In fact is checks for object identity if you give it a type object as the second argument.

is Bar.^lookup( 'Str' ).package, Bar, 'Bar defines Str'

You could always add a subroutine to add clarity.

sub defines-method (
Mu:U $class,
Str:D $method,
Str:D $desc = "$class.^name() defines $method"
) {
is $class.^lookup( $method ).?package, $class, $desc
}

defines-method Foo, 'Str';

You could alias it to an operator

sub &infix:<defines-method> = &defines-method;

Bar defines-method 'Str';

(Note that I used .?package in case .^lookup doesn't return anything.)


.^lookup gives you the Method object that will be called; so I don't know why you are talking about it giving you them in a different order when there is only one value returned. If there are multi methods it returns the proto method (possibly implicitly created).

If you want the individual multi methods you would call .candidates on it.

(There is also .^find_method, and off the top of my head I don't remember the difference)

I believe you are thinking of .can which gives you the Method objects in the order they would be called if you used .*Str or .+Str, which is the same as the method resolution order. Which means it would only change if you change the inheritance tree.

> class Bar is Str { method Str { 'Hello' } }

> quietly .perl.say for Bar.+Str;
"Hello"
""
""

> .perl.say for Bar.new.+Str
"Hello"
""
"Bar<80122504>"

> quietly .(Bar).perl.say for Bar.can('Str')
"Hello"
""
""

> .(Bar.new).perl.say for Bar.can('Str')
"Hello"
""
"Bar<86744200>"


Related Topics



Leave a reply



Submit