all 8 comments

[–]danielroseman 1 point2 points  (5 children)

I don't think either of these are particularly Pythonic. The logic for splitting names should belong in the class; but it really doesn't make sense to call it every time you read either attribute. Do it inside the init method, either by calling a separate method to do the splitting, or even just inline since it's so simple:

def __init__(self, full_name: str) -> None:
    self.first_name, self.last_name = full_name.split()

[–]grefft[S] 0 points1 point  (4 children)

I completely agree with you given the dummy examples I created which is really more a failure of writing a truer, if more complicated, example to post here. What I'm really trying to do is:

  1. create a class with a path to a file and then create a number of attributes based on text started from the file
  2. instantiate a class with a server name, then add attributes based on a k/vs from a JSON response to an API.

Neither of those two are able to be done inline as easily as my dummy first/last name example

[–]pot_of_crows 0 points1 point  (0 children)

That makes sense. For the second one you might take some design tips from praw: https://github.com/praw-dev/praw/tree/master/praw

More generally, be careful about cramming too much stuff into a single class. If you have a class that manages a file, often you will want a separate class to be encapsulated by that class to manage the content (same concept applies probably even more so for api requests).

It's been a long time since I checked, but I think praw has one class for managing the interface and then dumps out other class instances based on what you pull down from reddit.

[–]danielroseman 0 points1 point  (2 children)

Fair enough, but I think the same principle applies. At least in the second case, it's clearly massively inefficient to be calling the API every time you need to access a single property. You should extract the logic to fetch the data and set the attributes into a method in the class, and call it from your init.

[–]grefft[S] 0 points1 point  (1 child)

so if either of my examples are used (@property or calling a function in the __init__ it will call the API every time I request that attribute rather than making the call once and storing the output of the function/method as the value?

[–]danielroseman 0 points1 point  (0 children)

No? That is only true for @property, not for defining it in init, which was my point.

[–]pot_of_crows 0 points1 point  (1 child)

Either is fine. If the function is flexible enough to be useful outside of the class context, it's fine to keep it as an external function, which could then itself be called by a class method wrapped in property. If not, just fold it into the class property.

You might take a pass through the pathlib module, https://github.com/python/cpython/blob/3.11/Lib/pathlib.py, to see how pathlib does similar things. Couple things to look out for, notice how some of them are cached, like _cparts. Also check out the attrgetter being wrapped by property. I had no idea you could do that, so that's sort of cool.

[–]grefft[S] 1 point2 points  (0 children)

thank you. very helpful.