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
12 rats tied together
Sep 7, 2006

Seventh Arrow posted:

Oh! I think I see. So when you're using a capital "L" list, you should iterate with integers. But with strings, not so (even if you can slice them with, say, [4: ]). Am I correct?

My apologies, I misspoke by calling the string "a list", QuarkJets used the correct terminology by calling them "an iterable". The relationship both string and list have with indexes is that you can access elements in them with an integer or a slice (which is a shorthand for a specific range of integers). The thing I was hoping to have you notice (others have done a better job), is that it doesn't really make sense linguistically to pull the "Dth" or "Drd" element out of a list of numbered somethings.

There are other types of data structures where this might make sense, but not the list or the string.

Adbot
ADBOT LOVES YOU

12 rats tied together
Sep 7, 2006

awk handles this by collapsing "one or more" of the separator character, which seems like it would work here

you would probably be best served just ignoring the "pretty printed" header and just looking at the column labels themselves. you can generate the headers later from the parsed table data, if you need them

12 rats tied together fucked around with this message at 20:25 on Jun 27, 2022

12 rats tied together
Sep 7, 2006

if you want to end up with the list/dict/set anyway, always use a comprehension.

if you're using the comprehension as a terse way to obtain a specific iteration, and the iteration requires a gnarly looking comprehension, that's when you should consider unrolling. imo.

12 rats tied together
Sep 7, 2006

Seventh Arrow posted:

I've been learning about scopes and namespaces (and maybe not paying enough attention!), so I'm trying to understand how this works:



:siren::siren::siren: without getting into an internet slapfight about the practice of tipping :siren::siren::siren:, I'm not sure how the add_tip function is able to access the 'total' variable from total_bill. Isn't 'total' local to total_bill and thus inaccessible to add_tip? Shouldn't the parameter for add_tip be 'def add_tip(total_bill)'?

the 3rd statement passes the function called "add_tip" to the other function. the other function receives it and executes it, binding the result to a variable called "total"

this called "first class functions" or "higher order functions" or maybe some other things

e: adding quote for new page, and i have attempted to shittily mspaint this for you:


the key insight here is that a function is just an object like anything else in python, which means you can pass it around to other functions so it can be called later

12 rats tied together fucked around with this message at 05:20 on Aug 12, 2022

12 rats tied together
Sep 7, 2006

If you find that you end up sprinkling
Python code:
object.possibly_null_attr or default_value
all throughout your codebase like a magic salve, that's also probably an indication that you're missing some kind of GuaranteedObject class with a safe api so that your dependents can interact with it without needing to guard against the null propagating throughout the rest of your business logic

12 rats tied together
Sep 7, 2006

Falcon2001 posted:

This is obviously pretty abstracted, but is a high level summary of it. Double edit: this doesn't even contain what I asked about before. I might be an idiot. Anyway, feel free to critique this too!

At some point you need to ingest and handle the ugliness of reality, that part doesn't have to be particularly clean or clever or elegant as long as it works and its understandable. You're best equipped out of all of us here to write that part. :)

The important thing is that you fully handle it and provide a clean contract with the rest of your codebase, and it sounds like you have a good handle on that.

I would add that, in general ,I try not to rely on like "whatever I return will respond to __get_item__", I generally prefer creating a class that relates to a business logic concept and has methods with named parameters and docstrings that try to map english explanations of the business logic concepts to the programming language mechanisms for interacting with those constructs.

I don't think that's considered very pythonic, but python is at least partially an OO language so I feel like it's fine to rely on OO design principles when it makes sense.

12 rats tied together
Sep 7, 2006

A lot of the stuff QuarkJets mentioned is less Python and more "OOP in general", I think, so you're not likely to find a python resource that does a very good job of it. The best thing I'm aware of is honestly just, posting about it online, and reading about it online (especially what makes "good code" and how its different from bad code).

I think a good first step would be trying to refactor your loop into discrete problems that need to be solved and then writing a function that solves exactly one of those problems, a good example here is probably this one:

Python code:
                # is this already in the table?
                if con.execute("SELECT * FROM state_scores WHERE state_string = ?", (state_string,)).fetchall():
                    old_positions = old_positions + 1
                    to_drop[state_string] = True
                else:
                    new_positions = new_positions + 1
                    to_process[state_string] = pool.apply_async(process_state_string, (state_string,))
We can tell from the comment that we're looking at whether or not something was already processed(?), but then the code that follows immediately is manipulating various state containers you've constructed which just kind of exist arbitrarily.

You could change this to "if already_in_table(state_string)", but I think you were right not to, because it doesn't increase the at-a-glance readability of the code just to have the sqlite query live somewhere else. That's a sign that this code is doing too much, and that the level of abstraction here is too low --> not enough information is being hidden/is encapsulated by a logical construct with a name and a signature.

The more this happens, as you keep layering on new code paths and conditions, the harder it becomes over time to unwind the code and give it a name. We're basically at a point where, in order to suggest a potential refactor, we have to understand the entire codebase and the full context of the problem you're solving. Being good at OOP design is one way to combat this problem.

I gave it a read-through and honestly I can say that I just can't understand it enough to offer any specific advice, except that --
Python code:
    while len(to_process) > 0:
# [...]
        to_drop = {}
# [...]
            for i in to_drop:
                del to_process[i]
# [...]
            while len(to_process) > 0:
                to_drop = {} # !?
It reads really weirdly to me to nest two while loops inside of each other that have the same condition (to_process > 0) and that both immediately define a container for "stuff to drop". Do you think this could just be one while loop? I feel like if you could unroll that part, the rest of it would be much clearer.

12 rats tied together
Sep 7, 2006

A piece of the standard library that I personally reach for all the time when I have "collections of stuff to do" is the collections.deque, which you might find to be more ergonomic for your problem. Specifically, that you can create them, put things in them, and then get things out of them later. If you want to just handle things one at a time, the workflow is to your_deque.append() your stuff -> you can your_deque.pop() to get the "most recently appended" or you can your_deque.popleft() to get the "first appended".

I suspect that you can clean up a lot of your "list with index counter that gets incremented" and "dictionary that you check the length of and del items from" with a deque, since it can be looped on, and it mutates itself as you use it:
Python code:
>>> import collections
>>> dq = collections.deque()
>>> for x in range(15):
...   dq.append(x) 
... 
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
>>> while dq:
...   this_item = dq.pop() 
...   print(this_item)
... 
14
13
12
11
10 # and so on -- trimmed for readability
# an empty deque is also Falsey, which can help keep your conditional code branches clean and readable
>>> dq
deque([])
>>> bool(dq) 
False
Also you can use code=Python inside the code block to give yourself some syntax highlighting, which will help too. :)

12 rats tied together fucked around with this message at 20:11 on Sep 30, 2022

12 rats tied together
Sep 7, 2006

FredMSloniker posted:

Breaking every section out into its own function won't make get_state_complex look any nicer, and I'd have to define a function name for each possible section. I will be using functions for things sections have in common - for instance, 'offer three choices that only differ in what section we turn to next' or 'damage the character and turn to one section if they survive and another if they don't' - but, especially in "Impudent Peasant!", there's a lot of stuff that's unique to a single section that happens.

[...]
But Python doesn't let me say def sections["1"](state_string): It does let me store functions in dictionaries, but they have to be defined with a name first, which defeats the purpose - and lambda functions have to be a single line.

If you know a more Pythonic way to do what I'm doing, feel free to suggest it!

There's two main patterns I reach for when doing this, one of them you already touched on: put the functions in a dictionary. Just for completeness, that would look something like this:
Python code:
def handle_roll_luck(choices):
    print("Roll one die. Add 6 to the number and enter this total in the LUCK box.")
    for i in range(6):
        game_state["Initial LUCK"], game_state["LUCK"] = i + 7, i + 7
        add_choice(choices, f"You rolled a { i +1 }. Your initial LUCK score is { i +7 }")
    
    return "Roll STAMINA"

def handle_roll_stamina():
    print("Enter your initial SKILL score in the SKILL box on the Adventure Sheet.")
    print("Roll one die. Add 6 to the number rolled and enter this total in the STAMINA box.")

    return "Free Equipment"

game_state_handler_map = {
    "Roll STAMINA" = handle_roll_stamina,
    "Roll LUCK" = handle_roll_luck,
    "Free Equipment" = handle_free_equipment,
    # and so on -- up to "handle_page_350"
}

def walk_game_state():
    game_state = whatever

    while game_state['is_running']:
        next_state = game_state_handler_map[game_state['current_state']]()
        game_state['current_state'] = next_state
Right off the bat this is beneficial because your elif chain doesn't have to own "all possible behaviors". The functions do need to be defined, true, but that's normal (the behavior has to exist somewhere). Since they're functions, you could put them in a module and import them at the top of your "main" file, and then they're already in scope. If you don't want to deal with manually registering them in the dictionary, you can have a "game_state_functions" list in each of your modules, import the list, and loop on it to add each function to the dictionary with its string matcher.

Second pattern is basically the same except instead of manually pairing a string key to a function value, you can use structural pattern matching. This is useful because it gives you a clean, ergonomic way to interpret an input object (like your game state) and dispatch to an arbitrary code path while also performing setup for that code path. The example in the link is actually input handling for a text adventure game, you might find it somewhat relevant.

12 rats tied together
Sep 7, 2006

Truthy and Falsey values in python are well understood at this stage of the programming languages life, I think.

Checking if something is not None is a code smell though. Python is an OOP language so avoid if statements, avoid type checks, especially avoid writing if statements that are actually type checks.

Isolate the thing that is conditionally None and provide a safe API for it, don't let it propagate throughout your codebase.

The place where truthy and falsey provide the most value is while loops, because the important behavior lives in one place and the truthy or falseyness of it is trivial to verify in the language's excellent suite of repl and ide tooling.

12 rats tied together
Sep 7, 2006

Basically you want to get rid of the NoneTypes asap because they're, kind of by definition, the least useful type in the language. The simplest example I can think of is that "for x in response" where response is an empty list is valid code, but if response is None that's a runtime error.

What are you going to do with the optional string? Is there an appropriate default value for when the string isn't present? You can encode the answers to these questions permanently in your object model, which usually tends to simplify the code, improve readability and maintainability, and other good things.

12 rats tied together
Sep 7, 2006

Python handles the John Null situation in two ways:

1) None isn't actually "null", it is "the singleton instance of NoneType" which is an in-language placeholder for "the concept of nothing", but it actually is something, and that something has a (restrictive) API. This is different from other languages where Null might actually be nothing, it might be a macro that is a placeholder for "the integer 0", etc.

2) Python's NoneType casts to string as "None", so a John None would not run into any of these problems presuming that the handler for their name was running Python. They would correctly be stored as first_name John, last_name None, display_name John None.

As for default arguments, because the design depends so much on the business domain, there aren't a lot of good generic resources that I'm aware of. A non trivial but still toy example I like to pull out is, pretend we're writing doordash, and we have PickupOrder vs DeliveryOrder, and we're confused about what ValidateAddress() is doing. Which address is validated, or is it both? and what happens if we don't have a delivery address?

You solve this problem with OOP by extracting the things that vary, giving them names, and injecting them later at runtime. For this example, we might eventually conclude that DeliveryOrder and PickupOrder are fake, that we fooled ourselves into this failing pattern with names and inheritance, and that what we actually have here is an instance of the class FoodOrder that always has an instance of an object of the type FulfillmentBehavior, which always has ValidateAddress() (or some other wrapper method - a safe and stable API for the Order objects to consume).

In our FulfillmentBehavior object model we can design-away the NoneTypes and the optionals: PickupFulfillment never has a DeliveryDriver or a DeliveryAddress, but DeliveryFulfillment always does. Both objects can be talked-to by the parent FoodOrder in the same way, both objects always "do the right thing", nowhere in the code are we checking what the anything's type is, or double checking that things aren't None. If something were None unexpectedly, we'd raise an error in the constructor, and we'd never have completed a FoodOrder object to the point where it could possibly fail to actuate its ValidateAddress method. Helpfully, at the failure call site we also have all of the information we need to reconstruct it, so we can reproduce this easily later while debugging.

eXXon posted:

Huh? What is smelly about either of:
Python code:
if optional_arg is not None:
Python code:
if x := some_dict.get(key) is not None:
It's a code smell to be adding these things to an established codebase without it also being part of a PR that also introduces a new object model and all of its construction logic. To be clear, None exists, and you have to handle it sometime, but the places where you handle it should be some of the least changing, most understood parts of your codebase.

dict.get also makes me nervous, always. I'd rather see "if key in some_dict".

12 rats tied together
Sep 7, 2006

I mean, you didn't say anything wrong (that I remember anyway, I'm phone posting).

Handling nulls is hard in every language, and the tools that the language gives you to deal with them are important. As a strong/dynamic language Python doesn't have a lot to offer you, especially compared to Rust's compiler. You pretty much just have OOP design principles and, for some classes of null check, mypy (or similar).

If you post some examples later I will give you my $0.02 on them, you're ultimately the best person here to make the call though. Sometimes optional int is better than VerboseEncapsulatingTypeHierarchy.

12 rats tied together
Sep 7, 2006

eXXon posted:

Why does it make you nervous? It's less efficient to do key in dict followed by dict[key] if you actually need the value later on. It's also painful to write some comprehensions using dicts without walrus and get.

Think QuarkJets did a good job tackling the other cases, but this one is just personal preference from me: any time I'm using a raw dict, I want the "raise if accessing missing key" behavior of the subscript notation. Submitting a PR with .get(key, default) on a raw dict is a yellow flag for me because: Why don't we know if the key will be in there? And, how are we certain that the default value is acceptable in all cases, if we don't even know what keys are in there? Why does this function own the default value of a key instead of the dict constructor?

Sometimes it's fine, or you're working in an ecosystem where it's idiomatic to do this kind of stuff. That's fine, I'm not going to try and change e.g. django documentation from this thread. Speaking generally, if this is code your team owns, IME dict.get([...], default) is a canary signal for "something is wrong here and will get worse later".

WRT efficiency, I have not yet worked in a python codebase where we can't afford the extra O(1) complexity of performing an explicit "k in d", but I could be convinced to approve such a PR in a codebase that needed it.

12 rats tied together
Sep 7, 2006

Worth noting that Python has a pretty good interactive experience, right out of the box. If I run your code:
Python code:
>>> data = r.json()
Traceback (most recent call last):
  File "/home/rob/.pyenv/versions/3.10.4/lib/python3.10/site-packages/requests/models.py", line 910, in json
    return complexjson.loads(self.text, **kwargs)
  File "/home/rob/.pyenv/versions/3.10.4/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/home/rob/.pyenv/versions/3.10.4/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/home/rob/.pyenv/versions/3.10.4/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

# what is r?
>>> type(r)
<class 'requests.models.Response'>

# how can we talk to it?
>>> dir(r)
['__attrs__', '__bool__', '__class__', [...], 'content',  [...]  'status_code', 'text', 'url']

# r.content sounds promising
>>> r.content
b'<!DOCTYPE html>\n<!--[if IEMobile 7 ]> <html lang="en-US" class="no-js iem7"> <![endif]-->\n<!--[if lt IE 7]> <html class="ie6 lt-ie10 lt-ie9 lt-ie8 lt-ie7 no-js" lang="en-US"> <![endif]-->\n<!--[if IE 7]> # snip
This looks like HTML to me, so it's pretty reasonable that it would fail to parse as JSON.

12 rats tied together fucked around with this message at 20:53 on Nov 2, 2022

12 rats tied together
Sep 7, 2006

QuarkJets posted:

ITT I saw a good video posted about the pitfalls of OOP, it was a talk by this woman who I think was a Ruby guru? Does anyone else remember this? I feel like I'd like to watch it again, it was very good

Ruby guru + woman sounds like Sandi Metz, who I have posted about ITT, but she is pro-OOP in the form of Alan Kay's "OOP is mostly message passing", and against the improper use of inheritance.

The video I always link is from a talk she does called "nothing is something", and it is very good. The talk is basically an example of inheritance gone wrong, how to recognize it, and how to fix it.

https://www.youtube.com/watch?v=OMPfEXIlTVE

Apologies if this is not what you were thinking of. :)

12 rats tied together
Sep 7, 2006

To pass parameters to a function, you include them in the parenthesis that indicate to Python that you would like to call the function.

so, in the REPL,
Python code:
# take whatever the label "json" maps to right now and give me the result of executing its  __repr__ method
>>> json  

# take whatever the label "json" maps to and call it with no arguments/parameters
>>> json() 

# same as above but call it and pass it a string object that contains the characters in the word 'something'
>>> json('something')
(edited for formatting)

When you call "dir()" like that, it's just examining the current local scope. I would guess that there exists a "json" label in this scope because you've imported the json module.

To have "dir()" inspect an object, you need to feed it a label that points to that object, which you do by including the label in the parenthesis. The official documentation (above link) is a good source for this and should help clear up anything else you run into as well.

12 rats tied together fucked around with this message at 22:47 on Jan 6, 2023

12 rats tied together
Sep 7, 2006

the trick here is that python's split method splits by "one or more" blank space characters, just in case you ever had to do this yourself, you could write a loop that iterates through the string and finds "the first instance of the character to split on", and then just keep iterating forward until you find something else.

this is usually a lot easier than hoping that e.g. string index number 21 is the start of the next item. :)

12 rats tied together
Sep 7, 2006

Data Graham posted:

Yeah, I think that's how I think of it — just because Python isn't strongly typed doesn't mean you shouldn't impose contracts on your code yourself (e.g. with type hinting). Which means your function should only do one of two things: a) return two things, or b) raise an exception. Otherwise the calling code has to concern itself with all the weird possibilities of things the function might return, which defeats the whole purpose of factoring complex logic into simple callable methods.

Python is strongly typed but it's also dynamically typed - a label can point to a thing that is a different type than what it pointed to previously. Type systems have a lot of dimensions and they can get pretty confusing, for example, python is also "gradually typed" these days, to some degree it is "structurally typed" due to Protocols and also "duck typed".

But yes, you should absolutely use type hints when they matter or increase the readability of a particular section of code. Good, relevant example here is tuple unpacking:
Python code:
    lat: str
    lon: str
    for loc in str_locs:
        lat, long = loc

12 rats tied together
Sep 7, 2006

zipapp (standard library, official python website), shiv, and pex are the things to google if you want to get started packaging python applications with their dependencies

12 rats tied together
Sep 7, 2006

tqdm is fine. if you had to start from scratch, asyncio is pretty good these days

12 rats tied together
Sep 7, 2006

dictionary dispatch is a great pattern. if you're on a newer version of python (3.10+) you also have structural pattern matching. you should totally give this whole doc a read through, but this snippet in particular is especially relevant

Python code:
match command.split():
    case ["go", ("north" | "south" | "east" | "west") as direction]:
        current_room = current_room.neighbor(direction)
    case ["go", _]:
        raise Exception
if, in this case, the command was "go ;DROP TABLE [...]", none of the branches in the pattern match are acceptable (the 2nd element of command.split() would not be north, south, east, or west), so the wildcard "_" catches the match and raises an exception.

i'd probably still start by implementing dictionary dispatch, it seems like a much better fit for your current problem set of "how do I translate user input into a known-safe thing", but if you're writing user tools you should definitely check out the above link which has "handling user input" as a center-stage feature of the tutorial and examples.

12 rats tied together
Sep 7, 2006

student_grades['Andrew'][4] will get you that list item

if you want to loop through each item in the dictionary, the official docs have some guidance: https://docs.python.org/3/tutorial/datastructures.html#looping-techniques

12 rats tied together
Sep 7, 2006

Everything in itertools is delightful. Basically a standard library module tailor-made for trivializing coding interview screens.

12 rats tied together
Sep 7, 2006

assuming those "x" are multiplication i would guess you're supposed to realize that multiplication is commutative and that modulus isn't (?), so the way to maximize the value of U is to maximize the result of 1 % arr[n].

which seems weird because 1 % x is either going to be a 0, a 1, or a negative number. if you have two negative numbers you want to get the largest negative number you can out of it, otherwise, you want the smallest.

if you have zero negative numbers you just want to make sure arr[n] is nonzero, i guess? weird question

12 rats tied together
Sep 7, 2006

a dingus posted:

If Im understanding correctly maybe NewThing would be better off as a new type which is composed of Thing instead of inheriting it.

Agree with this, had half a reply typed out but cancelled it because I was phone posting.

From the examples/text posted it seems like there is just Thing and it has one of two different types of UpdateBehavior, so I would model it like that, making sure that I understand fully what the difference is between the UpdateBehaviors is and that I name them after it instead of having OldUpdateBehavior and NewUpdateBehavior.

12 rats tied together
Sep 7, 2006

WSL is pretty good but most of the "pretty good" is how easy it is to hook into vs code, so you just open code and your linux vm is sitting there in the integrated terminal waiting for you. It's probably also fine with PyCharm, but I've never used it so I can't vouch.

12 rats tied together
Sep 7, 2006

SSH is extremely good. another neat library in the realm of "python and ssh" is mitogen

12 rats tied together
Sep 7, 2006

I think I would probably model that as
Python code:
RecordsLoader.load(file="some_path", accumulator=lambda x, y: x + y, config=a_config_dict)
# or maybe
class AdditionAccumulator:
  vals = []
  @whatever
  def whatever:
    pass

class MultiplicationAccumulator:
  vals = []
  @whatever
  def whatever:
    pass

RecordsLoader.load(file="some_path", accumulator=AdditionAccumulator, config=b_config_dict)
Instead of creating a full class for a "named thing" that holds some behavior, create a class that holds the behavior directly, and is named what it does. Users can load records by composing various behaviors. Importantly, the "default behavior" is a fully realized thing, maybe its just called the DefaultAccumulator, instead of being a read-between-the-lines side effect of Bob subclassing aName and not overriding a method.

12 rats tied together
Sep 7, 2006

you could also define data_display inside main's scope. which is not usually recommended but is sometimes a useful pattern

12 rats tied together
Sep 7, 2006

1. pick 2 delimiters: one for key+value and one for element. split, split, assign.

2. use a markup language. yaml toml and json all allow mappings to be expressed in a single line. toml comes included in python but yaml is likely the most human readable. make sure to read the documentation for your yaml library because it does some type inference that might be surprising to you.

12 rats tied together
Sep 7, 2006

Your file is really just bytes. Python's `open()` function has configuration for how it should try to interpret the file -- if it should decode the bytes into strings / "words" or if it should just treat each line as bytes. The way to configure this is described here. If you did not specify binary mode, python is reading bytes off of the file system and doing this:

quote:

[...] in text mode (the default, or when 't' is included in the mode argument), the contents of the file are returned as str, the bytes having been first decoded using a platform-dependent encoding or using the specified encoding if given.
It's not really possible to store "an integer" or "a word" in a file. You can only write bytes, and then interpret them in a certain way.

As saintonan mentioned, Python is dynamically typed, so however you read the file you're free to convert the data from it into whatever type you need. You can tell what kind of a thing you have by feeding it into `type()`. You can cast a thing to a different type by feeding it into the built-in function for that type, for example, int(), str(), float().

Variables aren't really declared with a type either. A variable's name is just a label that gets attached to an object that lives in memory. The objects have the type, not the labels. Be careful that you don't misinterpret re-assigning a label as mutating an object:

Python code:
>>> word = "abcdef"
>>> type(word) # it's a string
<class 'str'>

>>> number = 123456
>>> type(number) # it's a number 
<class 'int'>

>>> word = number
>>> word
123456

>>> type(word) # its a number now.
<class 'int'>

>>> id(number)
2451604671088
>>> id(word) # they both point to the same object id, which was never mutated, and was always a number
2451604671088

12 rats tied together
Sep 7, 2006

Windows has scheduled tasks, linuxes will have cron. You would need a way to tell your scheduled script that a file has already been processed and should not be processed again (maybe move it out of your to-do folder if it successfully produces output).

There are thousands of ways to try and solve this problem but given that you're asking here, and considering the language you used to ask about it, I would probably start with scheduled tasks or cron.

A piece of advice I would offer is to run your automation against a copy of the data at first, even if you have to manually copy it. It's easy to accidentally break files.

12 rats tied together
Sep 7, 2006

pyenv + pyenv-alias plugin for isolated installs per project

12 rats tied together
Sep 7, 2006

I would probably build a lookup table of symbol to function and then use some combination of operator.methodcaller / operator.attrgetter to call them explicitly after parsing the text.

This seems like a bad idea, but I'm sure you know that already. :cheers:

12 rats tied together
Sep 7, 2006

I used pex for a thing and I liked it. We ended up shelving it and writing a rust version of the tool instead, and if we need to do more stuff in this area later, I have "try nuitka" on my todo list.

12 rats tied together
Sep 7, 2006

basically if you have to descend into a function because you didn't know it existed, that's fine, but if you have to bring something from inside it with you and then continue your search, it shouldn't have been a function

12 rats tied together
Sep 7, 2006

it depends but short answer probably not. if you wanted it to be accessible on that network you'd want to run it bound to an IP address in that network.

if pycharm, specifically, binds to 127.0.0.1, it's not routable, since that means "local computer" and it's ~not possible for a packet to arrive at your computer with that destination address.

if pycharm binds to 0.0.0.0 you can still access it through 127.0.0.1, but it would also be accessible on other addresses, which means it could theoretically be routed to you, which means it could theoretically be accessed by another device in that network. (if nothing blocked it first, like a firewall)

Adbot
ADBOT LOVES YOU

12 rats tied together
Sep 7, 2006

I would say to use pyright over mypy these days.

Also re: `list` vs `list[str]`, if you have a list of things that aren't all the same type, but they should all have some method, the thing for describing that is protocols. You have a list[Sendable], or list[CanSend], or something like that.

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