Instance of tuple has no member (pylint no-member) in enum class - python

I am getting the following errors from pylint when using the members "value" and "equals" from an enum class:
"code": "no-member"
"message": "Instance of 'tuple' has no 'value' member"
Versions:
pylint 2.3.1
astroid 2.2.5
Python 3.6.3
The code is executed as expected. I am just wondering if there might be something I am doing wrong (I am not a pro python programmer), or if there is a more "pythonic" way to achieve the same result:
from enum import Enum
class DefaultEnum(Enum):
def __new__(self,val,_name,_type):
obj = object.__new__(self)
obj._value_ = val
obj._publicName = _name
obj._type = _type
return obj
def __str__(self):
return self._publicName
def equals(self,_string):
return _string == str(self)
class GlobalEnum(DefaultEnum):
TRUE = 0,'True',str()
FALSE = 1,'False',str()
GlobalEnum.TRUE.value
>> 0
GlobalEnum.TRUE.equals('True')
>> True
repr(GlobalEnum.TRUE)
>> <GlobalEnum.TRUE: 0>
I am currently using the "# pylint: disable=no-member" comment to disable the warning, but I would prefer not to do this... The same goes for white-listing the class as I still would like pylint to report other findings.

To answer your main question:
pylint doesn't recognize dynamically-created attributes, and
Enum is "special" in a number of ways, one of them being that an Enum's members are actually instances of the enum class:
from enum import Enum
class MyEnum(Enum):
val1 = 0
print(type(MyEnum.val1)) # <enum 'MyEnum'>
class MyClass:
val1 = 0
print(type(MyClass.val1)) # <class 'int'>
That is, when you set TRUE in your GlobalEnum class, Python is converting TRUE into an instance of GlobalEnum, but pylint doesn't understand this, and since it looks like GlobalEnum.TRUE is being assigned a tuple value, pylint thinks it's a tuple, which has no "value" member.
To answer if there's a more "pythonic" way to achieve the same result, I'm not sure what you're trying to accomplish, but it looks like there are some weird things you're doing. For example:
__new__() gets passed a class as its first argument, but you're calling it "self," which by (near-)universal convention refers to an instance, which is very confusing to read. Typically one would call it cls.
Single leading underscores ("_name", "_type") by convention are usually used to denote "private" members, so it's going to be confusing to most readers to use them in function signatures. If you want to use a reserved word as a parameter name, a common convention is to use a trailing underscore (e.g., "type_", "exec_").
I'm not sure what you're trying to accomplish with your "_type" attribute, but right now both GlobalEnum.TRUE and GlobalEnum.FALSE will return an empty string as their _type, because str() returns a string instance, and without args the string will be empty. If you want it to return the str type, you should set it to str (without the parentheses).
I think what you're trying to do is to create an enum whose values will evaluate to True when compared against either an int or a string that you specify in the definition. In that case, instead of a user-defined equals() method (which you'll almost certainly forget to use at some point), you can override the built-in __eq__() magic method so that you can use the usual == operator instead:
from enum import Enum
class BaseEnum(Enum):
def __init__(self, int_value, str_value):
self._value_ = int_value
self.str_value = str_value
def __eq__(self, other):
return other == self.value or other == self.str_value
class MyEnum(BaseEnum):
TRUE = 0, 'True'
FALSE = 1, 'False'
print(MyEnum.TRUE == 0) # True
print(MyEnum.TRUE == 'True') # True
a = MyEnum.TRUE
print(a == MyEnum.TRUE) # True
print(MyEnum.TRUE.value) # 0
print(MyEnum.TRUE.str_value) # 'True'
[Note that str_value above is just a regular class property, meaning it can be set. To make it read-only, you can use a property decorator without a setter.]

Related

Type hinting enum member value in Python

For an enum Foo, How could one type hint a variable that must contain a member value (not the member itself) of some Enum-- e.g. a value such that Foo(x) will return a valid member of Foo?
Here's a simplified version of my motivating example:
class DisbursementType(Enum):
DISBURSEMENT = "disbursement"
REFUND = "refund"
ROLLBACK = "rollback"
class SerializedDisbursement(TypedDict):
transaction_type: ???
id: str
amount: float
a: SerializedDisbursement = {"transaction_type": "refund", id: 1, amount: 4400.24}
I would really like to avoid simply typeing transaction_type as Literal['disbursement', 'refund', 'rollback'] as that would be quite prone to getting out of synch over time.
The most widely compatible option is to just have an assertion that validates that the literal type doesn't go out of sync with the enum values:
class DisbursementType(enum.Enum):
DISBURSEMENT = "disbursement"
REFUND = "refund"
ROLLBACK = "rollback"
DisbursementValue = typing.Literal['disbursement', 'refund', 'rollback']
assert set(typing.get_args(DisbursementValue)) == {member.value for member in DisbursementType}
class SerializedDisbursement(typing.TypedDict):
transaction_type: DisbursementValue
id: str
amount: float
This ensures maximum compatibility with static analyzers, but requires repeating all member values. Also, the assertion cannot be checked statically.
Other options break static analysis. For example, if you use the functional API to create the enum from the literal type:
DisbursementValue = typing.Literal['disbursement', 'refund', 'rollback']
DisbursementType = enum.Enum('DisbursementType',
{name.upper(): name for name in typing.get_args(DisbursementValue)})
then mypy doesn't understand the enum, and at that point, there's little point having annotations at all.
Similarly, if you try to use non-literal type arguments for the Literal type:
DisbursementValue = typing.Literal[tuple(member.value for member in DisbursementType)]
then that breaks too.
While this doesn't answer the question as asked, I think you might also reconsider why you're using a TypedDict with a string instead of a proper class to hold the enum (instead of a str, since DisbursementType really does seem like an enum), and which can then employ some custom serialization logic.
For example:
import dataclasses as dc
import json
from enum import Enum
class Transaction(Enum):
DISBURSEMENT = "disbursement"
REFUND = "refund"
ROLLBACK = "rollback"
def __str__(self):
return self.value
#dc.dataclass
class Disbursement:
transaction: Transaction
id_: str
amount: float
def __str__(self):
return json.dumps(dc.asdict(self), default=str)
if __name__ == "__main__":
disbursement = Disbursement(
Transaction.REFUND,
"1",
4400.24,
)
print(disbursement)
$ mypy example.py
Success: no issues found in 1 source file
$ python3 example.py
{"transaction": "refund", "id_": "1", "amount": 4400.24}
Alternatively, you can have your enum inherit from str and simplify a few things:
import dataclasses as dc
import json
from enum import Enum
class Transaction(str, Enum):
DISBURSEMENT = "disbursement"
REFUND = "refund"
ROLLBACK = "rollback"
#dc.dataclass
class Disbursement:
transaction: Transaction
id_: str
amount: float
def __str__(self):
return json.dumps(dc.asdict(self))
if __name__ == "__main__":
disbursement = Disbursement(
Transaction.REFUND,
"1",
4400.24,
)
print(disbursement)
Other considerations:
Python 3.11 will have StrEnum: https://docs.python.org/3.11/howto/enum.html#strenum
Notes on subclassing enum: https://docs.python.org/3/library/enum.html#others
Equality comparisons with strings should probably be avoided: https://www.cosmicpython.com/blog/2020-10-27-i-hate-enums.html (prefer Disbursement.transaction == Transaction.REFUND to Disbursement.transaction == "refund" or str(Disbursement.transaction) == "refund"
Inheriting from (str, Enum) may have some drawbacks: How to deserialise enumeration with string representation?
Finally, I wanted to note that defining __str__ on my Enum did not do what I expected it to do using your TypedDict example above; that's because str(mydict) calls repr to provide each of mydict.values:
class Example:
def __repr__(self):
print("I called repr!")
return "from repr"
def __str__(self):
print("I called str!")
return "from str"
if __name__ == "__main__":
print(f"example: {Example()}\n")
d = {"example": Example()}
print(f"in a dict: {d}")
$ python3 foo.py
I called str!
example: from str
I called repr!
in a dict: {'example': from repr}
Additionally, you can't add custom methods to a TypedDict; if you change Example to inherit from TypedDict and rerun that last example, you'll see that neither __repr__ nor __str__ is called, and unfortunately there is no runtime error either (mypy helpfully warns error: Invalid statement in TypedDict definition; expected "field_name: field_type"). Because serialization logic seems to belong to Disbursement, I changed it to a somewhat similar class that allows me to customize its __str__: a dataclass.

How to create custom type validation function in Python? [duplicate]

I am using python 2.7.3 on Windows. I tried to override the __instancecheck__ magic method as a class method. But I can not make it work.
class Enumeration(int):
#classmethod
def __instancecheck__(cls, inst):
if type(inst) == cls:
return True
if isinstance(inst, int) and inst in range(0,10):
return True
return False
print isinstance(1, Enumeration) # prints False
print isinstance(1, Enumeration()) # prints True
I assume the first print statement would get True. But it seems the magic method __instancecheck__ is not called. And I don't know why the second print statement can work since the isinstance should take a class/type as the second parameter.
Does anyone know what the problem is? Thanks.
instancecheck must be defined in a metaclass:
class Enumeration(type):
def __instancecheck__(self, other):
print 'hi'
return True
class EnumInt(int):
__metaclass__ = Enumeration
print isinstance('foo', EnumInt) # prints True
Why is that? For the same reason why your second example worked. When python evaluates isinstance(A, B) it assumes B to be an object, looks for its class and calls __instancecheck__ on that class:
isinstance(A, B):
C = class-of(B)
return C.__instancecheck__(A)
But when B is a class itself, then its class C should be a class of a class, in other words, a meta-class!
The docs say:
Note that these methods are looked up on the type (metaclass) of a class. They cannot be defined as class methods in the actual class. This is consistent with the lookup of special methods that are called on instances, only in this case the instance is itself a class.
http://docs.python.org/2/reference/datamodel.html#customizing-instance-and-subclass-checks

python get all class level attributes on an instance, including parent class [duplicate]

Suppose we have the following class hierarchy:
class ClassA:
#property
def foo(self): return "hello"
class ClassB(ClassA):
#property
def bar(self): return "world"
If I explore __dict__ on ClassB like so, I only see the bar attribute:
for name,_ in ClassB.__dict__.items():
if name.startswith("__"):
continue
print(name)
Output is bar
I can roll my own means to get attributes on not only the specified type but its ancestors. However, my question is whether there's already a way in python for me to do this without re-inventing a wheel.
def return_attributes_including_inherited(type):
results = []
return_attributes_including_inherited_helper(type,results)
return results
def return_attributes_including_inherited_helper(type,attributes):
for name,attribute_as_object in type.__dict__.items():
if name.startswith("__"):
continue
attributes.append(name)
for base_type in type.__bases__:
return_attributes_including_inherited_helper(base_type,attributes)
Running my code as follows...
for attribute_name in return_attributes_including_inherited(ClassB):
print(attribute_name)
... gives back both bar and foo.
Note that I'm simplifying some things: name collisions, using items() when for this example I could use dict, skipping over anything that starts with __, ignoring the possibility that two ancestors themselves have a common ancestor, etc.
EDIT1 - I tried to keep the example simple. But I really want both the attribute name and the attribute reference for each class and ancestor class. One of the answers below has me on a better track, I'll post some better code when I get it to work.
EDIT2 - This does what I want and is very succinct. It's based on Eli's answer below.
def get_attributes(type):
attributes = set(type.__dict__.items())
for type in type.__mro__:
attributes.update(type.__dict__.items())
return attributes
It gives back both the attribute names and their references.
EDIT3 - One of the answers below suggested using inspect.getmembers. This appears very useful because it's like dict only it operates on ancestor classes as well.
Since a large part of what I was trying to do was find attributes marked with a particular descriptor, and include ancestors classes, here is some code that would help do that in case it helps anyone:
class MyCustomDescriptor:
# This is greatly oversimplified
def __init__(self,foo,bar):
self._foo = foo
self._bar = bar
pass
def __call__(self,decorated_function):
return self
def __get__(self,instance,type):
if not instance:
return self
return 10
class ClassA:
#property
def foo(self): return "hello"
#MyCustomDescriptor(foo="a",bar="b")
def bar(self): pass
#MyCustomDescriptor(foo="c",bar="d")
def baz(self): pass
class ClassB(ClassA):
#property
def something_we_dont_care_about(self): return "world"
#MyCustomDescriptor(foo="e",bar="f")
def blah(self): pass
# This will get attributes on the specified type (class) that are of matching_attribute_type. It just returns the attributes themselves, not their names.
def get_attributes_of_matching_type(type,matching_attribute_type):
return_value = []
for member in inspect.getmembers(type):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value.append(member_instance)
return return_value
# This will return a dictionary of name & instance of attributes on type that are of matching_attribute_type (useful when you're looking for attributes marked with a particular descriptor)
def get_attribute_name_and_instance_of_matching_type(type,matching_attribute_type):
return_value = {}
for member in inspect.getmembers(ClassB):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value[member_name] = member_instance
return return_value
You should use python's inspect module for any such introspective capabilities.
.
.
>>> class ClassC(ClassB):
... def baz(self):
... return "hiya"
...
>>> import inspect
>>> for attr in inspect.getmembers(ClassC):
... print attr
...
('__doc__', None)
('__module__', '__main__')
('bar', <property object at 0x10046bf70>)
('baz', <unbound method ClassC.baz>)
('foo', <property object at 0x10046bf18>)
Read more about the inspect module here.
You want to use dir:
for attr in dir(ClassB):
print attr
Sadly there isn't a single composite object. Every attribute access for a (normal) python object first checks obj.__dict__, then the attributes of all it's base classes; while there are some internal caches and optimizations, there isn't a single object you can access.
That said, one thing that could improve your code is to use cls.__mro__ instead of cls.__bases__... instead of the class's immediate parents, cls.__mro__ contains ALL the ancestors of the class, in the exact order Python would search, with all common ancestors occuring only once. That would also allow your type-searching method to be non-recursive. Loosely...
def get_attrs(obj):
attrs = set(obj.__dict__)
for cls in obj.__class__.__mro__:
attrs.update(cls.__dict__)
return sorted(attrs)
... does a fair approximation of the default dir(obj) implementation.
Here is a function I wrote, back in the day. The best answer is using the inspect module, as using __dict__ gives us ALL functions (ours + inherited) and (ALL?) data members AND properties. Where inspect gives us enough information to weed out what we don't want.
def _inspect(a, skipFunctionsAlways=True, skipMagic = True):
"""inspects object attributes, removing all the standard methods from 'object',
and (optionally) __magic__ cruft.
By default this routine skips __magic__ functions, but if you want these on
pass False in as the skipMagic parameter.
By default this routine skips functions, but if you want to see all the functions,
pass in False to the skipFunctionsAlways function. This works together with the
skipMagic parameter: if the latter is True, you won't see __magic__ methods.
If skipFunctionsAlways = False and skipMagic = False, you'll see all the __magic__
methods declared for the object - including __magic__ functions declared by Object
NOT meant to be a comprehensive list of every object attribute - instead, a
list of every object attribute WE (not Python) defined. For a complete list
of everything call inspect.getmembers directly"""
objType = type(object)
def weWantIt(obj):
#return type(a) != objType
output= True
if (skipFunctionsAlways):
output = not ( inspect.isbuiltin(obj) ) #not a built in
asStr = ""
if isinstance(obj, types.MethodType):
if skipFunctionsAlways: #never mind, we don't want it, get out.
return False
else:
asStr = obj.__name__
#get just the name of the function, we don't want the whole name, because we don't want to take something like:
#bound method LotsOfThings.bob of <__main__.LotsOfThings object at 0x103dc70>
#to be a special method because it's module name is special
#WD-rpw 02-23-2008
#TODO: it would be great to be able to separate out superclass methods
#maybe by getting the class out of the method then seeing if that attribute is in that class?
else:
asStr = str(obj)
if (skipMagic):
output = (asStr.find("__") == -1 ) #not a __something__
return (output)
for value in inspect.getmembers( a, weWantIt ):
yield value
{k: getattr(ClassB, k) for k in dir(ClassB)}
Proper values (instead of <property object...>) will be presented when using ClassB instance.
And of course You can filter this by adding things like if not k.startswith('__') in the end.

Enum of enums in Python?

Is it possible to have an enum of enums in Python? For example, I'd like to have
enumA
enumB
elementA
elementB
enumC
elementC
elementD
And for me to be able to refer to elementA as enumA.enumB.elementA, or to refer to elementD as enumA.enumC.elementD.
Is this possible? If so, how?
EDIT: When implemented in the naive way:
from enum import Enum
class EnumA(Enum):
class EnumB(Enum):
member = 0
print(EnumA)
print(EnumA.EnumB.member)
It gives:
<enum 'EnumA'>
Traceback (most recent call last):
File "Maps.py", line 15, in <module>
print(EnumA.EnumB.member)
AttributeError: 'EnumA' object has no attribute 'member'
You can't do this with the enum stdlib module. If you try it:
class A(Enum):
class B(Enum):
a = 1
b = 2
class C(Enum):
c = 1
d = 2
A.B.a
… you'll just get an exception like:
AttributeError: 'A' object has no attribute 'a'
This is because the enumeration values of A act like instances of A, not like instances of their value type. Just like a normal enum holding int values doesn't have int methods on the values, the B won't have Enum methods. Compare:
class D(Enum):
a = 1
b = 2
D.a.bit_length()
You can, of course, access the underlying value (the int, or the B class) explicitly:
D.a.value.bit_length()
A.B.value.a
… but I doubt that's what you want here.
So, could you use the same trick that IntEnum uses, of subclassing both Enum and int so that its enumeration values are int values, as described in the Others section of the docs?
No, because what type would you subclass? Not Enum; that's already your type. You can't use type (the type of arbitrary classes). There's nothing that works.
So, you'd have to use a different Enum implementation with a different design to make this work. Fortunately, there are about 69105 different ones on PyPI and ActiveState to choose from.
For example, when I was looking at building something similar to Swift enumerations (which are closer to ML ADTs than Python/Java/etc. enumerations), someone recommended I look at makeobj. I forgot to do so, but now I just did, and:
class A(makeobj.Obj):
class B(makeobj.Obj):
a, b = makeobj.keys(2)
class C(makeobj.Obj):
c, d = makeobj.keys(2)
print(A.B, A.B.b, A.B.b.name, A.B.b.value)
This gives you:
<Object: B -> [a:0, b:1]> <Value: B.b = 1> b 1
It might be nice if it looked at its __qualname__ instead of its __name__ for creating the str/repr values, but otherwise it looks like it does everything you want. And it has some other cool features (not exactly what I was looking for, but interesting…).
Note The below is interesting, and may be useful, but as #abarnert noted the resulting A Enum doesn't have Enum members -- i.e. list(A) returns an empty list.
Without commenting on whether an Enum of Enums is a good idea (I haven't yet decided ;) , this can be done... and with only a small amount of magic.
You can either use the Constant class from this answer:
class Constant:
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
Or you can use the new aenum library and its built-in skip desriptor decorator (which is what I will show).
At any rate, by wrapping the subEnum classes in a descriptor they are sheltered from becoming members themselves.
Your example then looks like:
from aenum import Enum, skip
class enumA(Enum):
#skip
class enumB(Enum):
elementA = 'a'
elementB = 'b'
#skip
class enumC(Enum):
elementC = 'c'
elementD = 'd'
and you can then access them as:
print(enumA)
print(enumA.enumB)
print(enumA.enumC.elementD)
which gives you:
<enum 'enumA'>
<enum 'enumB'>
enumC.elementD
The difference between using Constant and skip is esoteric: in enumA's __dict__ 'enumB' will return a Constant object (if Constant was used) or <enum 'enumB'> if skip was used; normal access will always return <enum 'enumB'>.
In Python 3.5+ you can even (un)pickle the nested Enums:
print(pickle.loads(pickle.dumps(enumA.enumC.elementD)) is enumA.enumC.elementD)
# True
Do note that the subEnum doesn't include the parent Enum in it's display; if that's important I would suggest enhancing EnumMeta to recognize the Constant descriptor and modify its contained class' __repr__ -- but I'll leave that as an exercise for the reader. ;)
I made an enum of enum implementing de __ getattr __ in the base enum like this
def __getattr__(self, item):
if item != '_value_':
return getattr(self.value, item).value
raise AttributeError
In my case I have an enum of enum of enum
class enumBase(Enum):
class innerEnum(Enum):
class innerInnerEnum(Enum):
A
And
enumBase.innerEnum.innerInnerEnum.A
works
You can use namedtuples to do something like this:
>>> from collections import namedtuple
>>> Foo = namedtuple('Foo', ['bar', 'barz'])
>>> Bar = namedtuple('Bar', ['element_a', 'element_b'])
>>> Barz = namedtuple('Barz', ['element_c', 'element_d'])
>>> bar = Bar('a', 'b')
>>> barz = Barz('c', 'd')
>>> foo = Foo(bar, barz)
>>> foo
Foo(bar=Bar(element_a='a', element_b='b'), barz=Barz(element_c='c', element_d='d'))
>>> foo.bar.element_a
'a'
>>> foo.barz.element_d
'd'
This is not a enum but, maybe solves your problem
If you don't care about inheritance, here's a solution I've used before:
class Animal:
class Cat(enum.Enum):
TIGER = "TIGER"
CHEETAH = "CHEETAH"
LION = "LION"
class Dog(enum.Enum):
WOLF = "WOLF"
FOX = "FOX"
def __new__(cls, name):
for member in cls.__dict__.values():
if isinstance(member, enum.EnumMeta) and name in member.__members__:
return member(name)
raise ValueError(f"'{name}' is not a valid {cls.__name__}")
It works by overriding the __new__ method of Animal to find the appropriate sub-enum and return an instance of that.
Usage:
Animal.Dog.WOLF #=> <Dog.WOLF: 'WOLF'>
Animal("WOLF") #=> <Dog.WOLF: 'WOLF'>
Animal("WOLF") is Animal.Dog.WOLF #=> True
Animal("WOLF") is Animal.Dog.FOX #=> False
Animal("WOLF") in Animal.Dog #=> True
Animal("WOLF") in Animal.Cat #=> False
Animal("OWL") #=> ValueError: 'OWL' is not a valid Animal
However, notably:
isinstance(Animal.Dog, Animal) #=> False
As long as you don't care about that this solution works nicely. Unfortunately there seems to be no way to refer to the outer class inside the definition of an inner class, so there's no easy way to make Dog extend Animal.
Solution based on attrs. This also allows to implement attributes validators and other goodies of attrs:
import enum
import attr
class CoilsTypes(enum.Enum):
heating: str = "heating"
class FansTypes(enum.Enum):
plug: str = "plug"
class HrsTypes(enum.Enum):
plate: str = "plate"
rotory_wheel: str = "rotory wheel"
class FiltersTypes(enum.Enum):
bag: str = "bag"
pleated: str = "pleated"
#attr.dataclass(frozen=True)
class ComponentTypes:
coils: CoilsTypes = CoilsTypes
fans: FansTypes = FansTypes
hrs: HrsTypes = HrsTypes
filter: FiltersTypes = FiltersTypes
cmp = ComponentTypes()
res = cmp.hrs.plate
Try this:
# python3.7
import enum
class A(enum.Enum):
def __get__(self, instance, owner):
return self.value
class B(enum.IntEnum):
a = 1
b = 2
class C(enum.IntEnum):
c = 1
d = 2
# this is optional (it just adds 'A.' before the B and C enum names)
B.__name__ = B.__qualname__
C.__name__ = C.__qualname__
print(A.C.d) # prints: A.C.d
print(A.B.b.value) # prints: 2

Is there a way to access __dict__ (or something like it) that includes base classes?

Suppose we have the following class hierarchy:
class ClassA:
#property
def foo(self): return "hello"
class ClassB(ClassA):
#property
def bar(self): return "world"
If I explore __dict__ on ClassB like so, I only see the bar attribute:
for name,_ in ClassB.__dict__.items():
if name.startswith("__"):
continue
print(name)
Output is bar
I can roll my own means to get attributes on not only the specified type but its ancestors. However, my question is whether there's already a way in python for me to do this without re-inventing a wheel.
def return_attributes_including_inherited(type):
results = []
return_attributes_including_inherited_helper(type,results)
return results
def return_attributes_including_inherited_helper(type,attributes):
for name,attribute_as_object in type.__dict__.items():
if name.startswith("__"):
continue
attributes.append(name)
for base_type in type.__bases__:
return_attributes_including_inherited_helper(base_type,attributes)
Running my code as follows...
for attribute_name in return_attributes_including_inherited(ClassB):
print(attribute_name)
... gives back both bar and foo.
Note that I'm simplifying some things: name collisions, using items() when for this example I could use dict, skipping over anything that starts with __, ignoring the possibility that two ancestors themselves have a common ancestor, etc.
EDIT1 - I tried to keep the example simple. But I really want both the attribute name and the attribute reference for each class and ancestor class. One of the answers below has me on a better track, I'll post some better code when I get it to work.
EDIT2 - This does what I want and is very succinct. It's based on Eli's answer below.
def get_attributes(type):
attributes = set(type.__dict__.items())
for type in type.__mro__:
attributes.update(type.__dict__.items())
return attributes
It gives back both the attribute names and their references.
EDIT3 - One of the answers below suggested using inspect.getmembers. This appears very useful because it's like dict only it operates on ancestor classes as well.
Since a large part of what I was trying to do was find attributes marked with a particular descriptor, and include ancestors classes, here is some code that would help do that in case it helps anyone:
class MyCustomDescriptor:
# This is greatly oversimplified
def __init__(self,foo,bar):
self._foo = foo
self._bar = bar
pass
def __call__(self,decorated_function):
return self
def __get__(self,instance,type):
if not instance:
return self
return 10
class ClassA:
#property
def foo(self): return "hello"
#MyCustomDescriptor(foo="a",bar="b")
def bar(self): pass
#MyCustomDescriptor(foo="c",bar="d")
def baz(self): pass
class ClassB(ClassA):
#property
def something_we_dont_care_about(self): return "world"
#MyCustomDescriptor(foo="e",bar="f")
def blah(self): pass
# This will get attributes on the specified type (class) that are of matching_attribute_type. It just returns the attributes themselves, not their names.
def get_attributes_of_matching_type(type,matching_attribute_type):
return_value = []
for member in inspect.getmembers(type):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value.append(member_instance)
return return_value
# This will return a dictionary of name & instance of attributes on type that are of matching_attribute_type (useful when you're looking for attributes marked with a particular descriptor)
def get_attribute_name_and_instance_of_matching_type(type,matching_attribute_type):
return_value = {}
for member in inspect.getmembers(ClassB):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value[member_name] = member_instance
return return_value
You should use python's inspect module for any such introspective capabilities.
.
.
>>> class ClassC(ClassB):
... def baz(self):
... return "hiya"
...
>>> import inspect
>>> for attr in inspect.getmembers(ClassC):
... print attr
...
('__doc__', None)
('__module__', '__main__')
('bar', <property object at 0x10046bf70>)
('baz', <unbound method ClassC.baz>)
('foo', <property object at 0x10046bf18>)
Read more about the inspect module here.
You want to use dir:
for attr in dir(ClassB):
print attr
Sadly there isn't a single composite object. Every attribute access for a (normal) python object first checks obj.__dict__, then the attributes of all it's base classes; while there are some internal caches and optimizations, there isn't a single object you can access.
That said, one thing that could improve your code is to use cls.__mro__ instead of cls.__bases__... instead of the class's immediate parents, cls.__mro__ contains ALL the ancestors of the class, in the exact order Python would search, with all common ancestors occuring only once. That would also allow your type-searching method to be non-recursive. Loosely...
def get_attrs(obj):
attrs = set(obj.__dict__)
for cls in obj.__class__.__mro__:
attrs.update(cls.__dict__)
return sorted(attrs)
... does a fair approximation of the default dir(obj) implementation.
Here is a function I wrote, back in the day. The best answer is using the inspect module, as using __dict__ gives us ALL functions (ours + inherited) and (ALL?) data members AND properties. Where inspect gives us enough information to weed out what we don't want.
def _inspect(a, skipFunctionsAlways=True, skipMagic = True):
"""inspects object attributes, removing all the standard methods from 'object',
and (optionally) __magic__ cruft.
By default this routine skips __magic__ functions, but if you want these on
pass False in as the skipMagic parameter.
By default this routine skips functions, but if you want to see all the functions,
pass in False to the skipFunctionsAlways function. This works together with the
skipMagic parameter: if the latter is True, you won't see __magic__ methods.
If skipFunctionsAlways = False and skipMagic = False, you'll see all the __magic__
methods declared for the object - including __magic__ functions declared by Object
NOT meant to be a comprehensive list of every object attribute - instead, a
list of every object attribute WE (not Python) defined. For a complete list
of everything call inspect.getmembers directly"""
objType = type(object)
def weWantIt(obj):
#return type(a) != objType
output= True
if (skipFunctionsAlways):
output = not ( inspect.isbuiltin(obj) ) #not a built in
asStr = ""
if isinstance(obj, types.MethodType):
if skipFunctionsAlways: #never mind, we don't want it, get out.
return False
else:
asStr = obj.__name__
#get just the name of the function, we don't want the whole name, because we don't want to take something like:
#bound method LotsOfThings.bob of <__main__.LotsOfThings object at 0x103dc70>
#to be a special method because it's module name is special
#WD-rpw 02-23-2008
#TODO: it would be great to be able to separate out superclass methods
#maybe by getting the class out of the method then seeing if that attribute is in that class?
else:
asStr = str(obj)
if (skipMagic):
output = (asStr.find("__") == -1 ) #not a __something__
return (output)
for value in inspect.getmembers( a, weWantIt ):
yield value
{k: getattr(ClassB, k) for k in dir(ClassB)}
Proper values (instead of <property object...>) will be presented when using ClassB instance.
And of course You can filter this by adding things like if not k.startswith('__') in the end.

Categories

Resources