Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 505
Smule Avatar asked Aug 21 '19 07:08

Smule


People also ask

What if tuple has no'__name'member?

The error nodes.py:17:24: E1101: Instance of 'tuple' has no '__name' member (no-member) should not appear for the tuple of Node s. pylint should treat the sum of 2 tuples as a tuple and not as a sequence of tuples.

Why does Pylint think globalenum is a tuple instead of a class?

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.

Why is Pylint giving me an error that Y is not a member?

In this case Pylint will give you an error that y is not a member of A . Automating the finding of such bugs in your tooling is very useful. However since Python is so dynamic there's a whole bunch of ways in which you can dynamically define members of a class, and Pylint won't always catch these.

Is it possible to dynamically define members of a class in Pylint?

However since Python is so dynamic there's a whole bunch of ways in which you can dynamically define members of a class, and Pylint won't always catch these. Pylint is smart enough to not emit any errors in this particular case, which is correct since this program will run:


1 Answers

To answer your main question:

  1. pylint doesn't recognize dynamically-created attributes, and
  2. 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:

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

  2. 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_").

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

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

like image 153
dizzy77 Avatar answered Oct 11 '22 06:10

dizzy77