Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Falcon2001 posted:

So I have a bit of a best practices question. In the event that you have a function that mutates a data structure like a list/etc, is it best practice to return the mutated version, or to just make it clear in the docstring that the function mutates the object? For the sake of argument, let's assume that this isn't a library or other externally facing function and is pretty tightly bound business logic specific to a situation, where you're separating code out into functions to improve readability/etc.

code:
big_dict = retrieve_data()
remove_butts(big_dict)
vs
code:
big_dict = retrieve_data()
big_dict = remove_butts(big_dict)

My opinion is that the first is better over the second example. All things being the same (documented mutation behavior, etc) returning a value from a function immediately makes me think away from mutation. In your second example its fine since you're reassigning to the same variable, but if you weren't (or your peer didn't read the docstring and assumed the return was a copy) and assigned it to buttless_big_dict, you now have two references to the same dict.

The first example is less ambiguous in that it doesn't return anything (or if it does, you don't care because you're not capturing it in a variable), and looks closer to a function that just does something with or to the arguments you passed.

As a bonus, here's a third option I'd prefer:
Python code:
class BigDict:
    def __init__(self):
        self.data = retrieve_data()

    def remove_butts(self):
        """This motherfucker mutates `data`."""
        # remove butts ...

big_dict = BigDict()
big_dict.remove_butts()  # mutation, clear as day

Adbot
ADBOT LOVES YOU

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Falcon2001 posted:

Thanks for the feedback! I think the biggest takeaway is that it should be clear, and I properly type hint all my functions/etc, so it's clear that this doesn't return anything (-> None), which means I think I'm going in the right direction.

I also agree with the nicest option being the 'make it a class and then have a method that does the thing to itself'. This is a four year old codebase that I just came into, so there's a few different places we're just passing dictionaries around instead of using dataclasses/etc. Some of those I'll be trying to clean up as I go along and generally docstring or otherwise fix up some of the older bits of this program.

Godspeed goon

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Protocol7 posted:

Thought someone would get a kick out of this terrible fstring I wrote today.



Remind me how I get paid to write Python again? :stonklol:

Apparently this works
Python code:
guid = "1234-123123-12123-1234"
print(f"{{{guid}}}")
# > {1234-123123-12123-1234}
lol

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Today I learned that the Ellipsis ... in python is an actual object that can be used to mean I don't care

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


worms butthole guy posted:

Hey all, I have another simple question that i'm hoping someone can point me in the right direction.

How do you test if a JSON key exists in Python and if not, move on?


So I am using Flask Mail to send out a email. It looks like this:


code:
f"""
Reservation Process: {HubspotData['properties']['reservation_process']['value']}
Pool: {HubspotData['properties']['pool']['value']}
"""
The problem is, if someone didn't fill out that field, the app will crash instead of just continuing on. Is there a way to rectify this? With PHP and Javascript I can use something like:

code:
"Reservation Process:{HubspotData['properties']['reservation_process'] ? {HubspotData['properties']['reservation_process']['value']} : null}"
Thanks!

When you say a JSON key in python do you mean a dict? Assuming HubspotData is a dict (or at least easily convertible to one) the preferred way to look-before-getting a key is HubspotData.get(“key”, default_value).

If expressions (the ternary operator equivalent in python) will also work with a caveat: if the value is falsy you won’t get what you expect.

ef;b

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


12 rats tied together posted:

Other people have been doing a good job of fielding these questions, but I wanted to try and provide a zoomed-out explanation for you real quick because virtualenvs are one of those things that are easiest to understand if you start at the bottom (IMO), but it's hard to find resources online that actually start at the bottom and don't presume some foreknowledge on your part.
  • In order to run python stuff, your computer needs to be able to know where "python.exe" is (assuming you're on windows).
  • Your python.exe is preconfigured to look for python-related files inside a certain set of directories, some more info on this can be found here, but you don't have to fully understand it.
  • One of the folders your python is configured to look in is called "site-packages". Some info here, but it's not all the info and you don't have to fully understand it either.
  • When you "pip install something", a folder named "something" will be downloaded to your site-packages folder.
  • When you "import something", python scans your site-packages folder for a folder named "something", and then tries to run an __init__.py that exists inside that folder.
A virtualenv is like if you made isolated copies of these site-packages folders and swapped them around behind the scenes. In order to "use" a virtualenv, then, you need to tell Python about it in some way, so it knows to look in one site-packages instead of another. On the shell you do this by "activating" it.

VS Code is slightly more complicated and your best bet will be to read the documentaton for the python extension, which explains how VS Code finds your environments and how it decides which one to activate for you.

You could also do it by hand instead, and move these directories around yourself, but that would suck and you'd probably want to script it.

Good info here.

I use VSCode personally and can expand a bit here. To activate a virtual environment in vscode you need the python extension installed. Then CMD/Ctrl + Shift + P to pull up the command palette and select Python: Select Interpreter… then open your virtual environment which should be listed there. If it isn’t try the Developer: Reload Window command first from the command palette.

Your vscode should now recognise your virtual env and give you nice autocomplete etc for your locally installed packages.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


D34THROW posted:

Probably more too much docstring. I use numpydoc-style docstrings because I have an easier time visually parsing them, so I have parameters, returns, notes, and examples sections. It's a template function to generate the class attribute for an HTML tag that depends on the session["ADMIN_MODE"], essentially a shortcut for a big jinja if/else block that exists to assign the proper class for admin or user mode.



I mean, if that's too much docstring, I'm more than open to advice on how to pare it down. I want to contribute more to open-source projects and stuff like that so a lesson on brevity is welcome.

Python is pretty flexible when it comes to how exhaustive the docstring needs to be. I tend to use the 6 month/peer rule: how complicated would it be to use this function as me 6 months from now/as another user or peer?

If there are a fair amount of gotchas or lots of parameters, you’ll want a good exhaustive docstring, there’s no real upper limit to how helpful you want to be here. Otherwise, you’ll find that a succinct passage is enough, especially for functions that take none or one or two params and are like 5 lines in length. If you have empathy for your fellow users then you’ll find the right balance no problem.

(e: docstrings in a lot of pythons own api can be pretty succinct and short if you take a look)

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


SirPablo posted:

Someone explain or direct me somewhere... What does the "-> str" do when defining the function?

-> at the end of the function hints at the return type. -> str means it returns a string.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


SirPablo posted:

Ah interesting. So it's almost a comment of sorts, doesn't actually do anything functionally by itself.

Yeah, python is still ducktyped so you can go against your own hints. Like D34THROW says, they can be used by libs like mypy to actually enforce types by giving you errors on a pre-build.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Hughmoris posted:

Rookie question as I try to wrap my head around Docker and containers:

Say I have a new install of Ubuntu and want to try out the latest Python Docker image. Do I develop my script INSIDE the running Python container, or do I develop on my local machine and pass my script as a parameter when I run the container?

The easiest way is to skip building images everytime with new versions of your script. To do that you bind the dir of your script with -v into a volume the container can reach. The entrypoint command will always be the same too. Then when you spin the container up it will always be the same image just running the script you bound from your host machine.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Gangsta Lean posted:

You’re probably missing out on a ton of stuff by assuming that there’s an interior list. This form is probably way more common.

code:
[x + y for y in range(10, 50, 10) for x in range(5)]

i cannot wrap my head around this form

e: well this is understandable actually. last time i came across this double for was for zipping two things or whatever and didn’t get it at the time

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


SirPablo posted:

Is there a way to scrape threads?

Sure, you can just make the same requests as your browser does with requests and scrape the content with beautifulsoup. Id drop a line to astral first though.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Falcon2001 posted:

Is there a simpler way to handle this interaction? I'm finding I'm doing this a reasonable amount.

Python code:
expected_var = object.possibly_null_attr if object.possibly_null_attr else default_value

You can use the built in getattr, similar syntax to dict.get("key", default).

Python code:
expected_var = getattr(object, "possibly_null_attr", default_value)
That is, if you mean null to mean the attribute doesn't exist. If it exists and is None, then it'll return that None, in which case you can:
  • Test for all falsy, at which point Data Graham's suggestion will work;
  • Test only for None which will require you to do what you are doing (recommend you add is None to your if)

Armitag3 fucked around with this message at 19:14 on Aug 31, 2022

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


The March Hare posted:

I've been in OPs position before when writing systems to ingest absolutely insane API data.

I'm talking folks who implemented their database layer in such a way that an odd ID canonically indicated one record subtype while an even ID indicated the other. They were also quite fond of indicating that any field was null in several different ways, of indicating errors either in the field, in an object in place of the field's normal type, or sometimes in a totally separate error array - all of which could, of course, be null in different ways and none of which seemed to follow any kind of pattern.

Not saying that is what OP is dealing with here, but it serves as one example of when you might need to do a lot of weird type comparisons.

I'm getting flashbacks to ingesting SOAP requests - that of course could have been any of dozens of operations - in a single endpoint, which meant a conga line of null checks because of course every single param was optional to support any arbitrary combination. We couldn't count on anything.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


FISHMANPET posted:

I'm writing a Python module, and I've gotten myself stuck into some circular dependencies. I've found some "basic" stuff about fixing that, but I seem to have gotten myself into a deeper pickle. Basically, I have a User class, and that User class has methods that will return a Group. And I have a Group class that has methods that will return a User. And I'm trying to define these in separate files, otherwise this is gonna be some massive 2000 line single-file module. I've finally just "thrown in the towel" so to speak and stopped importing at the top of the file, and instead am importing only when a function is called, but that doesn't feel great either.

So any tips or links on how to "design" my way out of this?

look up lazy loading or lazy importing. Basically in this kind of circular dependency you’d want to only import the symbols when actually needed, i.e. when you call the methods.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


rowkey bilbao posted:

Anyone familiar with Pydantic v2 ?

You used to be able to do this:
Python code:
import uuid
from typing import Any, Dict, Union

from pydantic import UUID4,UUID5, BaseModel, Field
from pydantic import __version__ as pdversion
from pydantic import validator


def uuid_factory():
    return str(uuid.uuid4())


class Pet(BaseModel):
    name: str
    owners_id:Union[UUID4, UUID5]


class PetOwner(BaseModel):
    person_id: Union[UUID4, UUID5] = Field(default_factory=uuid_factory)
    name: str
    animals: Dict[str, Pet]

    @validator("animals", pre=True)
    def enforce_dog_properties(cls, v, values, **kwargs):
        print("enforce_dog_properties()")
        for name, animal in v.items():
            if "owners_id" not in animal:
                animal["owners_id"] = values['person_id'] # we set some value on the pet based on an attribute on the owner
        return v

print(f"using pydantic v{pdversion}")
a = PetOwner(name="henry", animals={"animal_1": {"name": "sultan"}}) # we don't need to set the Pet.owners_id just now
print(a.json())
"""
{"person_id": "e3a81267-0d13-4f76-8183-1e9443e843cc", "name": "henry", "animals": {"animal_1": {"name": "sultan", "owners_id": "e3a81267-0d13-4f76-8183-1e9443e843cc"}}}
"""
Now validator is deprecated and the recommended replacement is field_validator. Replacing the validator method with
Python code:

    @field_validator('animals', mode="before")
    def enforce_dog_properties(cls, v):
       for critter_id, critter_specs in v.items():
          if "owners_id" not in critter_specs:
              critter_specs["owners_id"] = cls.person_id
       return v
doesn't work, because the person_id value isn't set.
How do can I create instances like in my first examples, while being able to set values in the Pet instance based on the PetOwner.person_id value ?

I've been trying field_validator, but I run into similar issues.

Try adding the @classmethod decorator to your validator function, then you should have access to cls as you're expecting.

Python code:

    @field_validator('animals', mode="before")
    @classmethod
    def enforce_dog_properties(cls, v):
       for critter_id, critter_specs in v.items():
          if "owners_id" not in critter_specs:
              critter_specs["owners_id"] = cls.person_id
       return v

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


It's called fluent interface design, and it's not a bad idea at all, especially applied to situations where the flow can read logically from one operation to the next.
Python code:
# 1 + 2 - 3 + 4
result = calculator.add(1).add(2).subtract(3).add(4).result()

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


from them import frustration

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


LochNessMonster posted:

Is anyone doing the advent of code by any chance?

I started with day 1 and while my code works fine for the test data set it’s returning a wrong anwser for the real data set.

I’m getting a file with a string of alohanumeric data per line. I need to get the first and last number of each line and combine them. “bein3bdj4bdn9” would mean 3 and 9, making 39. This was fairly simple. I used a regex to match all digits in a list and concat the first/last items.

Part 2 was where the data set could also include written numbers (one, two, etc). Tuned the regex to include those which worked fine on the test data set.

On the real data set the code runs with no problems, but the number I get is rejected by AoC saying it’s wrong. I’ve been looking at samples and they all seem to be correctly taking the first and last number from the string, spelled out number or digit.

Any ideas on how to troubleshoot such an issue would be appreciated.

Yeah this is a common trip up right on day one that got me too. Without giving the game away here’s two things you should be aware:
* some words might overlap (“twone”)
* regex-replacing, given the above, might give you two results depending on which direction you do it from (“2ne” vs “tw1”)

Don’t give up the elves need you

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


LochNessMonster posted:

Thanks, I didn’t take the overlap into account. Coming up with a solution is going to be challenging, but interesting.

How did you find out this was the problem?

My solution involved reversing the string (I’m doing it in C++ this year). When I ran it with the string already reversed, I got different results and that’s when i realised that it matters which way you parse the string from first.

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Zoracle Zed posted:

Maybe check out the 'sliding_window' iterator from the itertools recipes, which continues to drive me insane for not being importable but instead requires copy-pasting.

Hey now thanks for this

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Vulture Culture posted:

Other than the input variable shadowing a builtin, which has the potential to create gnarly bugs later on, these aren't the kinds of things new devs, or arguably any devs, really need to spend cognitive energy on. (IMO, all internal utility function names are bullshit no matter how much you pretend they aren't. Beyond a small handful of developers, none of them will ever see actual reuse.)

I still avoid using id as an attribute of a model because of that best practice, but yeah I don't think I've ever seen id() being used in the wild.

Adbot
ADBOT LOVES YOU

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Deadite posted:

Perfect, thank you. And I'm sure that team doesn't do any unit testing. I got pulled in to help out because they were running behind. At the end of the month this program will not be my problem again until an ironic reorg forces me to maintain it.

Here's a more accurate representation of the issue, but pretend there are about 20 more variables that need to be tested:

Python code:
v = 'N'
w = 'N'
x = 'a'
y = 3

if (v == 'N' or w == 'N') and (not ((x == 'd' and y == 3) or (x in ['a', 'b', 'c']))):
    print('do something')
else:
    print('do something else')

What version of Python are you running this on? (Lol I know, it's probably going to be 3.8) I ask just on the offchance you might be able to leverage pattern matching to make this less of a rats nest. (https://docs.python.org/3.10/reference/compound_stmts.html#the-match-statement)

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply