2

Recently I defined one of my Python classes as shown below.

from datetime import datetime, date, time
import enums


class ExampleClass:

    defaults = (-1, "", "", datetime.today(), "", -1, [], "", -1, "", "", [], "")

    def __init__(self, **kwargs):
        count = 0
        for ex in enums.ExampleEnums:
            setattr(self, ex.name, kwargs.get(ex.value, ExampleClass.defaults[count]))
            count += 1

    def __str__(self):
        return_string = "Example Object with "
        count = 0
        for val in enums.ExampleEnums:
            if (getattr(self, val.name) != ExampleClass.defaults[count]):
                return_string += str("%s: %s, " % (val.name, getattr(self, val.name)))
            count += 1
        return return_string[:-2]

    def __repr__(self):
        return_string = ""
        count = 0
        for val in enums.ExampleEnums:
            if (getattr(self, val.name) != ExampleClass.defaults[count]):
                return_string += str("%s=%s, " % (val.value, getattr(self, val.name)))
            count += 1
        return return_string[:-2]

    def __eq__(self, other):
        for val in enums.ExampleEnums:
            if (getattr(self, val.name) != getattr(other, val.name)):
                return False
        return True

    def __ne__(self, other):
        for val in enums.ExampleEnums:
            if (getattr(self, val.name) == getattr(other, val.name)):
                return False
        return True

Anyway, I'm wondering: is this a good way to write a class definition for a data class? Are there any ways I could improve this? I don't need any code, just generalities are fine as I'm only posting this as a way to see how I can improve my own coding abilities in Python.

Thanks

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
Woody1193
  • 7,252
  • 5
  • 40
  • 90
  • I'm glad that my suggestions are well received, but please don't edit the original code in your question (see [What you may and may not do after receiving answers](http://meta.codereview.stackexchange.com/a/1765)). – mkrieger1 Jun 19 '15 at 11:37
  • 1
    @mkrieger1 - that meta post is specific to Code Review, and Stack Overflow has different systems in place. – rolfl Jun 19 '15 at 11:38
  • @rolfl Oh, I just saw it. But don't the same (or at least similar) principles apply here? – mkrieger1 Jun 19 '15 at 11:40
  • Actually, isn't this whole question more suited for Code Review than for Stack Overflow? – mkrieger1 Jun 19 '15 at 11:43
  • 1
    It strikes me that this whole question is off-topic because there's no specific programming question.... and, the whole question should have been asked on Code Review, but, be that as it may, no, the same principles do not apply here - though often I think they should. – rolfl Jun 19 '15 at 11:44
  • Do not incorporate the answers into your original question, except as a new *Edited* portion that you add; otherwise, the answers don't make any sense. – Ethan Furman Mar 09 '16 at 08:15

3 Answers3

2

You use this pattern several times (shown here is __init__, it applies to __str__ and __repr__ as well):

count = 0
for ex in enums.ExampleEnums:
    setattr(self, ex.name, kwargs.get(ex.value, ExampleClass.defaults[count]))
    count += 1

It would be better to iterate directly over the items in ExampleClass.defaults instead of manually counting an index. This can be achieved using zip:

for ex, default in zip(enums.ExampleEnums, ExampleClass.defaults):
    setattr(self, ex.name, kwargs.get(ex.value, default))

The __eq__ method can be simplified using all:

def __eq__(self, other):
    return all(getattr(self, val.name) == getattr(other, val.name)
               for val in enums.ExampleEnums)

Then, as others have already said, you can express __ne__ in terms of __eq__, or even using the == operator:

def __ne__(self, other):
    return not self == other
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
1

You can call self._eq__(other) in function __ne__.

Quill
  • 2,729
  • 1
  • 33
  • 44
Will
  • 792
  • 1
  • 5
  • 22
0

The best way to write a data class will vary on use case. But on what you've presented, you shouldn't repeat code. Once you've defined the __eq__ operator, you should be using that elsewhere. (What if your definition of __eq__ changes?) Also, you don't have to define every magic method under the sun... just the things that are valuable to you.

Check out this guide to Python's magic methods: http://www.rafekettler.com/magicmethods.html

Also see this answer on __ne__ vs __eq__ and how to define them: Python implementing ne operator based on eq.

You should also look into decorators (and @property, specifically).

Community
  • 1
  • 1
  • Thanks for your response. As was mentioned above, the `__ne__` operator was changed to reflect its symmetry with the `__eq__` operator. Further, I'm not defining them all, just the ones that will probably be useful to me or that I will use often enough that I want to explicitly state their operation. Finally, before this I was using `@property` but I don't particularly care about them being private, I just didn't like writing out the same sort of definitions over and over again. – Woody1193 Jun 19 '15 at 10:55