3

Many of my classes look like the following class to represent accounts

class Account(object):
    def __init__(self, first, last, age, id, balance):
        self.first = first
        self.last = last
        self.age = age
        self.id = id
        self.balance = balance

    def _info(self):
        return self.first, self.last, self.age, self.id, self.balance

    def __eq__(self, other):
        return self._info == other._info()

    def __hash__(self):
        return hash((type(self), self.info()))

    def ... # other methods follow

But really the only relevant information is the list of attributes I care about first, last, age, id, balance. Is there a standard method to define Python classes that follow this structure?

At first glance I thought of namedtuple but I'm not sure that that allows me to add additional methods after the fact. Really, I want something like the following

class Account(object):
    attributes = "first last age id balance"

    def ... # other methods

What is the best way of obtaining this?

MRocklin
  • 48,441
  • 20
  • 124
  • 196

3 Answers3

4

Not sure how idiomatic it is, but the following satisfies your requirements:

class Slottable:
    def __init__(self, *args):
        for slot, arg in zip(self.slots.split(' '), args):
            setattr(self, slot, arg)

    def _info(self):
        return tuple(getattr(self, attr) for attr in self.slots.split())

    def __eq__(self, other):
        return self._info() == other._info()

    def __hash__(self):
        return hash((type(self), self._info()))


class Account(Slottable):
    slots = "first last age id balance"

    def fullname(self):
        return self.first + " " + self.last

matt = Account("Matthew", "Smith", 28, 666, 1E6)
john = Account("John", "Jones", 46, 667, 1E7)

d = {matt: 5, john: 6}  # Hashable

print matt.fullname()
#=> "Matthew Smith"
print john.fullname()
#=> "John Jones"
print matt == matt, matt == john
#=> True False
matt.age = 29  # Happy birthday!
print matt.age
#=> 29
JohnJ
  • 4,213
  • 2
  • 26
  • 38
  • Hrm, this doesn't handle mutation well. In particular it doesn't follow the Single Point of Truth principle. Redundant data is held both in `__info` and in each of the attributes. – MRocklin Jun 14 '13 at 19:47
  • @MRocklin Mutation? Why would you want that?!? :-) I edited the example to handle mutation and keep the info only in the attributes dict. – JohnJ Jun 14 '13 at 20:28
1

Here are some recipes you can try: override __setattr__, __dict__, __slots__ and/or init. Let us know what works for you.

Community
  • 1
  • 1
Brent Washburne
  • 11,417
  • 4
  • 51
  • 70
0

Many libraries out there exist to cover this need: attrs, dataclasses, pydantic, ... and my new addition to this landscape, pyfields.

Choice will mainly depend on the features you need or do not need. pyfields focuses on fields definition and optional validation and conversion, without any constraint on your class. Fields that can be native become as fast as python native attributes can be, while fields requiring callbacks (validators/converters) are implemented using descriptors.

You can blend your own constructor with the

from pyfields import field, init_fields

class Account(object):
    first = field(doc="first name")
    last = field(doc="last name")
    age = field(doc="the age in years")
    id = field(doc="an identifier")
    balance = field(doc="current balance in euros")

    @init_fields
    def __init__(self, msg):
        print(msg)

a = Account("hello, world!", first="s", last="marie", age=135, id=0, balance=-200000)
print(vars(a))

yields

hello, world!
{'balance': -200000, 'id': 0, 'age': 135, 'last': 'marie', 'first': 's'}

As opposed to other, more "all in one" libraries, pyfields concentrates on the fields and the constructor only, with a "minimum viable product" spirit. So if you would also like dict representation and conversion, hash, equality and comparison, you should add them on top of your class using another library. I am currently developing a mixture lib providing mix-in classes for this, with the same philosophy of a la carte features - that you will be able to use with or without pyfields.

See pyfields documentation for details. Do not hesitate to provide feedback !

smarie
  • 2,470
  • 15
  • 24