2
\$\begingroup\$

Say you have a Python class for something that has an optional gender field. You'd like a helper property/method to get the appropriate pronoun for an instance. You'd also like to be able to get the possessive version of the pronoun. The use for these helpers will be in creating human-readable emails and other such messages related to the object.

Below are three ways to do it. Which is best in terms of style and design and why?

Option 1

@property def pronoun(self): return "he" if self.gender == 'male' else "she" if self.gender == 'female' else "they" @property def possessive_pronoun(self): return "his" if self.gender == 'male' else "her" if self.gender == 'female' else "their" 

Option 2

def pronoun(self, possessive=False): if possessive: return "his" if self.gender == 'male' else "her" if self.gender == 'female' else "their" else: return "he" if self.gender == 'male' else "she" if self.gender == 'female' else "they" 

Option 3

def pronoun(self, possessive=False): pronouns = ("he", "his") if self.gender == 'male' else ("she", "her") if self.gender == 'female' \ else ("they", "their") return pronouns[1] if possessive else pronouns[0] 

Feel free to suggest an even better fourth option.

P.S. In case you are wondering, I like to use double quotes for strings meant for human consumption, single quotes otherwise.

\$\endgroup\$
1
  • \$\begingroup\$ In any case, the correct spelling seems to be "possessive" and not "possesive". \$\endgroup\$ Commented Jan 3, 2014 at 17:20

4 Answers 4

3
\$\begingroup\$

Option 1 looks to be best in my opinion as it enforces readability of the intent in the instance usage, e.g.

u.subject_pronoun.capitalize() + " is buying the new widget for " + u.possesive_pronoun + " own use." 

Versus:

u.pronoun().capitalize() + " is buying the new widget for " + u.pronoun(True) + " own use." 

In the latter case the meaning is lost on the possessive pronoun since the consumer of the class didn't supply the argument name.

Another approach to getting the values by a nested dictionary (similar to the Ruby way suggested by Josay) would be:

self._pronouns = { 'male': { 'possessive': 'his', 'object': 'him', 'subject': 'he' }, 'female': { 'possessive': 'hers', 'object': 'her', 'subject': 'she' }, 'unspecified': { 'possessive': 'theirs', 'object': 'them', 'subject': 'they' } } @property def object_pronoun(self): return self._pronouns[self.gender]['object'] if self.gender else self._pronouns['unspecified']['object'] def possessive_pronoun(self): return self._pronouns[self.gender]['possessive'] if self.gender else self._pronouns['unspecified']['possessive'] def subject_pronoun(self): return self._pronouns[self.gender]['subject'] if self.gender else self._pronouns['unspecified']['subject'] 

This approach allows for more types to be added easily as shown by adding the subject type.

Edit: Updated per comment suggestions.

\$\endgroup\$
2
  • \$\begingroup\$ I agree, option 1 is the best of the three interfaces. I would go even further and rename pronounnominative_pronoun. There should be another case as well: object_pronoun. (You seem to have changed nominative case in the question to objective case in your answer.) \$\endgroup\$ Commented Jan 3, 2014 at 21:46
  • \$\begingroup\$ You've used the key plural for the third case (their / theirs). But the OP indicates that the gender field is optional, so the third case is really unspecified or other, not plural. \$\endgroup\$ Commented Jan 3, 2014 at 21:51
3
\$\begingroup\$

I usually try to use data structure over code whenever it's possible. It usually make things shorter, easier to read and easier to update.

If I had to write such a thing, I'd probably write something like (untested code but just to show the idea) :

pronouns = { 'male' => ("he","his"), 'female' => ("she", "her") } def pronoun(self, possessive=False): return pronouns.get(self.gender, default=("they","their"))[possessive] 

If we could be sure that self.gender has only 3 different values, the default value could be put in the pronouns dictionnary straightaway (associated to "plural" for instance).

Different variations could be written. I guess one could use defaultdict to ensure that we always retrieve the default value. I don't like this solution that much as the dictionnary would get bigger as we try to retrieve using invalid keys.

I don't know if there's a (simple) way to have a dictionnary which returns a default value when the key is not present without updating the dictionnary but I'd be interested in such a thing.

\$\endgroup\$
2
  • \$\begingroup\$ => should be :? and can't use possessive directly as a key value, right? \$\endgroup\$ Commented Jan 3, 2014 at 22:57
  • 1
    \$\begingroup\$ That's Ruby syntax. \$\endgroup\$ Commented Jan 4, 2014 at 2:35
2
\$\begingroup\$

From a style perspective I like the first option; if you're treating this as a property its clear what you intend

For implementation, I'd try a class level default with an instance override to provide the right words to allow for specialization or extension (for other languages than English, say, or use at Oberlin College). Whatever your gender politics this implementation is extensible to new cases and one offs.

class Gendered(object): NOMINATIVE = {'m':('he', 'they'), 'f': ('she','they'), 'n':('it', 'they')} ACCUSATIVE = {'m':('him', 'them'), 'f': ('her','them'), 'n':('it', 'them')} POSSESSIVE = {'m':('his', 'their'), 'f': ('hers', 'their'), 'n':('its', 'their')} def __init__(self, word, gender = 'n', plural = 0, **kwargs): self.Word = word self.Gender = gender.lower()[0] self.Plural = 0 if not plural else 1 self.Nominative = kwargs.get('nominative', self.NOMINATIVE) self.Accusative = kwargs.get('accusative', self.ACCUSATIVE) self.Posessive = kwargs.get('possessive', self.POSSESSIVE) def get_pronoun(self, case ): # where case will be one of self.Nominative. self.Accusative, self.Possesive return case.get(self.Gender, ('',''))[self.Plural] # using get rather than if-checking lets you specify a default @property def pronoun(self): return self.get_pronoun(self.Nominative); @property def accusative_pronoun(self): return self.get_pronoun(self.Accusative); @property def possessive_pronoun(self): return self.get_pronoun(self.Posessive); example = Gendered("battleship", "f", False) print example.pronoun print example.accusative_pronoun print example.possessive_pronoun example = Gendered("king", "m", possessive = {'m':('his royal', 'their royal'), 'f':('her royal', 'their royal'), 'n':('their', 'their royal')}) print example.pronoun print example.accusative_pronoun print example.possessive_pronoun 
\$\endgroup\$
1
\$\begingroup\$

As it's no longer 2014, it's possible to use dataclasses:

@dataclass class Pronouns: subject: str object: str possessive: str @classmethod def they_their(cls): return cls("they", "them", "their") @classmethod def she_her(cls): return cls("she", "her", "hers") @classmethod def he_him(cls): return cls("he", "him", "his") 

You'd probably use this as a component of another class:

@dataclass class BarPatron: names: List[str] pronouns: Pronouns description: str @property def they(self): return self.pronouns.subject @property def them(self): return self.pronouns.object @property def their(self): return self.pronouns.possessive billiam = BarPatron( ["William", "Bill", "Billy", "Mac", "Buddy"], Pronouns.he_him(), "man", ) def sing_verse_one(patron): print("\"All I want to do is have a little fun before I die,\"") print(f"Says the {patron.description} next to me, out of nowhere") print(f"It's apropos of nothing, but {patron.they} says " f"{patron.their} name is {patron.names[0]}") print(f"But I'm sure it's {' or '.join(patron.names[1:])}") 

(The they\them\their properties maybe ought to go on the Pronouns class - the interface could be improved for sure!)

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.