Try the inspect module. getmembers and the various tests should be helpful.
EDIT:
For example,
class MyClass(object):
a = '12'
b = '34'
def myfunc(self):
return self.a
>>> import inspect
>>> inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
[('__class__', type),
('__dict__',
<dictproxy {'__dict__': <attribute '__dict__' of 'MyClass' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
'a': '34',
'b': '12',
'myfunc': <function __main__.myfunc>}>),
('__doc__', None),
('__module__', '__main__'),
('__weakref__', <attribute '__weakref__' of 'MyClass' objects>),
('a', '34'),
('b', '12')]
Now, the special methods and attributes get on my nerves- those can be dealt with in a number of ways, the easiest of which is just to filter based on name.
>>> attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
>>> [a for a in attributes if not(a[0].startswith('__') and a[0].endswith('__'))]
[('a', '34'), ('b', '12')]
...and the more complicated of which can include special attribute name checks or even metaclasses ;)
Answer from Matt Luongo on Stack Overflowpython - Getting attributes of a class - Stack Overflow
Python function attributes - uses and abuses - Stack Overflow
class - Difference between methods and attributes in python - Stack Overflow
What is the difference in python attributes with underscore in front and back - Stack Overflow
Videos
Try the inspect module. getmembers and the various tests should be helpful.
EDIT:
For example,
class MyClass(object):
a = '12'
b = '34'
def myfunc(self):
return self.a
>>> import inspect
>>> inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
[('__class__', type),
('__dict__',
<dictproxy {'__dict__': <attribute '__dict__' of 'MyClass' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
'a': '34',
'b': '12',
'myfunc': <function __main__.myfunc>}>),
('__doc__', None),
('__module__', '__main__'),
('__weakref__', <attribute '__weakref__' of 'MyClass' objects>),
('a', '34'),
('b', '12')]
Now, the special methods and attributes get on my nerves- those can be dealt with in a number of ways, the easiest of which is just to filter based on name.
>>> attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
>>> [a for a in attributes if not(a[0].startswith('__') and a[0].endswith('__'))]
[('a', '34'), ('b', '12')]
...and the more complicated of which can include special attribute name checks or even metaclasses ;)
def props(cls):
return [i for i in cls.__dict__.keys() if i[:1] != '_']
properties = props(MyClass)
I typically use function attributes as storage for annotations. Suppose I want to write, in the style of C# (indicating that a certain method should be part of the web service interface)
class Foo(WebService):
@webmethod
def bar(self, arg1, arg2):
...
then I can define
def webmethod(func):
func.is_webmethod = True
return func
Then, when a webservice call arrives, I look up the method, check whether the underlying function has the is_webmethod attribute (the actual value is irrelevant), and refuse the service if the method is absent or not meant to be called over the web.
I've used them as static variables for a function. For example, given the following C code:
int fn(int i)
{
static f = 1;
f += i;
return f;
}
I can implement the function similarly in Python:
def fn(i):
fn.f += i
return fn.f
fn.f = 1
This would definitely fall into the "abuses" end of the spectrum.
Terminology
Mental model:
- A variable stored in an instance or class is called an attribute.
- A function stored in an instance or class is called a method.
According to Python's glossary:
attribute: A value associated with an object which is referenced by name using dotted expressions. For example, if an object o has an attribute a it would be referenced as o.a
method: A function which is defined inside a class body. If called as an attribute of an instance of that class, the method will get the instance object as its first argument (which is usually called
self). See function and nested scope.
Examples
Terminology applied to actual code:
a = 10 # variable
def f(b): # function
return b ** 2
class C:
c = 20 # class attribute
def __init__(self, d): # "dunder" method
self.d = d # instance attribute
def show(self): # method
print(self.c, self.d)
e = C(30)
e.g = 40 # another instance attribute
A method is an attribute, but not all attributes are methods. For example, if we have the class
class MyClass(object):
class_name = 'My Class'
def my_method(self):
print('Hello World!')
This class has two attributes, class_name and my_method. But only my_method is a method. Methods are functions that belong to your object. There are additional hidden attributes present on all classes, but this is what your exercise is likely talking about.
Recently I more than once stumbled upon code where classes where defined such that they were initialized with an underscore attribute. These classes then also contain a property to return that attribute without an underscore. See this example:
class Character(object): """ Defines the character class """ def __init__(self, name): """ Initialize the class """ self._name = name @property def name(self): """ Name of the character """ return self._name
What is the advantage over just initializing it with self.name instead of self._name?
TLDR: The attribute is the value produced by looking up the attribute name.
According to the Python Language Reference, Attribute references, in the statement
value = obj.name
obj.name is an attribute reference, name is the attribute name, and the produced value is the attribute. Note that value is the attribute in this case because it was produced by the attribute reference, not because it is inherently related. For example, an attribute reference may produce a new value every time it is evaluated.
The primary must evaluate to an object of a type that supports attribute references, which most objects do. This object is then asked to produce the attribute whose name is the identifier. This production can be customized by overriding the
__getattr__()method. If this attribute is not available, the exception AttributeError is raised. Otherwise, the type and value of the object produced is determined by the object. Multiple evaluations of the same attribute reference may yield different objects.
In an attribute assignment such as
obj.name = value
the value does not necessarily become the attribute. If the attribute name points to a data descriptor, value may be stored, modified or discarded.
First let's talk about general objects in Python. The documentation defines 3 characteristics for every object:
- identity
An object’s identity never changes once it has been created; you may think of it as the object’s address in memory.
- type
An object’s type determines the operations that the object supports (e.g., “does it have a length?”) and also defines the possible values for objects of that type. The type() function returns an object’s type (which is an object itself). Like its identity, an object’s type is also unchangeable.
- value
The value of some objects can change. Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable.
Understanding objects means understanding types, but types are objects also. These TypeObjects are one of the most important structures in Python and define the basic functionality of an object. Specifically they implement the getattr and setattr functions.
The default behavior for this implementation is described here:
The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance,
a.xhas a lookup chain starting witha.__dict__['x'], thentype(a).__dict__['x'], and continuing through the base classes oftype(a)excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. [...]
the methods for an class or an instance which can overwrite the default behaviour are __get__, __set__ and __delete__.
The documentation for the __get__ method specifies:
This method should return the computed attribute value or raise an AttributeError exception.
Summarizing all the above, my formal definition for an attribute would be along the lines of:
- attributes are objects with identity, type and value
- they are owned by another object
Further reading:
https://docs.python.org/3/reference/datamodel.html
https://docs.python.org/3/c-api/typeobj.html
https://docs.python.org/3/howto/descriptor.html?highlight=descriptor
https://realpython.com/python-descriptors/