|
e: adding quote since this is a new pageFalcon2001 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. IMHO you should return a copy of the original that is mutated. I wouldn't muck about in caller state without it being an explicit requirement, since otherwise it seems more fragile for no benefit. Ideally "remove_butts" can be expressed directly in the caller state as a dict comprehension, or similar, and it does not need to be a function though.
|
# ? Mar 22, 2022 20:11 |
|
|
# ? May 28, 2024 16:28 |
|
Protocol7 posted:I think that's kind of up to your preference. Though if you are not returning anything then you should be very clear that the function does not return anything and thus mutates an argument as a side effect. It shouldn't do both; either return a new object or mutate the existing one. I like to do both, personally: mutating the input and also returning it. So long as the behavior is consistent I think that's what really matters (if you have a bunch of list mutators, they should all have the same behavior)
|
# ? Mar 22, 2022 20:41 |
|
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. 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:
|
# ? Mar 22, 2022 20:52 |
|
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.
|
# ? Mar 22, 2022 21:00 |
|
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. Godspeed goon
|
# ? Mar 22, 2022 21:01 |
|
QuarkJets posted:I like to do both, personally: mutating the input and also returning it. So long as the behavior is consistent I think that's what really matters (if you have a bunch of list mutators, they should all have the same behavior) I was curious so I looked it up. The docs say that they try to avoid doing both in the standard library. quote:Some operations (for example y.append(10) and y.sort()) mutate the object, whereas superficially similar operations (for example y = y + [10] and sorted(y)) create a new object. In general in Python (and in all cases in the standard library) a method that mutates an object will return None to help avoid getting the two types of operations confused. So if you mistakenly write y.sort() thinking it will give you a sorted copy of y, you’ll instead end up with None, which will likely cause your program to generate an easily diagnosed error. https://docs.python.org/3/faq/programming.html#why-did-changing-list-y-also-change-list-x Not really much of an official style guideline on it that I could find though. And of course it's a bit different when calling an instance method like y.sort() IMO. Macichne Leainig fucked around with this message at 22:19 on Mar 22, 2022 |
# ? Mar 22, 2022 22:14 |
|
When mutating I personally prefer to return self than return None, since the former enables the opportunity for method chaining. But that's a much more prominent thing in Ruby than Python, and the Python style used in the stdlib favors returning None for mutations. Even if you return self, you definitely don't need to reassign it in the caller though. Returning a mutated copy is a pattern you'll find frequently in the stdlib, especially among built-in functions that take itetables as inputs. That makes a lot of sense when working with generic data structures but any methods that operate on specific types (i.e., your business logic) probably wouldn't make sense to return copies outside of an explicit copy() operation. Oh, if you have a top-level function that mutates a dict because your legacy code base doesn't do classes I wouldn't return the dict parameter, just let it fall off with None. ExcessBLarg! fucked around with this message at 00:33 on Mar 23, 2022 |
# ? Mar 23, 2022 00:20 |
|
QuarkJets posted:There's a lot of extraneous stuff going on here and I don't really understand what you want to do, sorry. Here's a simple example that flips a QLabel between two images, is that kind of what you're looking for? Let me be more descriptive. This is the code snippet that includes what happens when my pokedex app can't find a pokemon in the api: code:
Notice the red blinking light? I want the yellow light to do the same thing when the function I posted earlier runs. My goal is to have the yellow light keep blinking until the "extract" function is finished. I plan on doing this by creating a "while loop" for the function that has the light blinking function run until the extract code gets to "self.loading = False". At the end. However, when running the application, this is what occurs: As you can see it goes straight to Data.complete_light_on(self) at the end. I decided to experiment and instead of having the loading light load a few times in a range and putting it in the code as such: code:
But the result I get is the yellow light blinks only after all the other code in the function has ran: That's my issue. I want the light to blink as long as the extract function is running and stop when it isn't in order to tell the user the data is loading.
|
# ? Mar 23, 2022 01:25 |
|
Protocol7 posted:I was curious so I looked it up. The docs say that they try to avoid doing both in the standard library. Seems reasonable to me In numpy, functions always (?) return a new array, and then some functions will also let you provide the output array, and this may be the input array for in-place mutation. That's a weird set of cases where a mutation occurs on the input only when you ask for that, but an output is always returned
|
# ? Mar 23, 2022 01:30 |
|
punk rebel ecks posted:My goal is to have the yellow light keep blinking until the "extract" function is finished. See Use PyQt's QThread to Prevent Freezing GUIs. ExcessBLarg! fucked around with this message at 01:45 on Mar 23, 2022 |
# ? Mar 23, 2022 01:43 |
|
ExcessBLarg! posted:If the extract function takes "a long time" to run, and you're running it on the same thread as your Qt event loop, then those QTimers won't fire until the function is done and the event loop runs again. While this does seem to be what I'm looking for, it's a bit too advanced for me. Not QThread as a concept, but the fact that I'm doing so many things in the button click at once it will be difficult to know how to separate it all into various functions for the worker threads. I may just edit the yellow light out and use the loading idea for a future project.
|
# ? Mar 23, 2022 04:58 |
|
punk rebel ecks posted:While this does seem to be what I'm looking for, it's a bit too advanced for me. Yeah the trick is that Qt runs an event loop in the background and it sounds like you're combining IO into the same event that handles the GUI alterations
|
# ? Mar 23, 2022 05:59 |
|
It's still very difficult to diagnose what's going on here. What kind of object is Data? Why is self being passed to it? What does Data.run_api do? What invokes extract()? Is extract() part of a QDialog class or something else? There could be a lot of reasons that are causing you to see what you're seeing, basically. Places like stackoverflow have a rule that sums up as "post complete examples" for this reason, it's probably impossible to say exactly what's going on here using only what's shown. You could try working from the example that I wrote earlier, where a QTimer just flips an image between off and on. Try doing that as the very first step of your method, to ensure that it's in Qt's event loop. Disable anything else that actually does IO and just get the light blinking. Then add in more until it breaks, and you'll learn more about what the code is really doing. That may help guide you to a solution. code:
|
# ? Mar 23, 2022 09:18 |
|
There was a video on type hints in the past couple of years where some popular library went through their rollout of it with MyPy and other type checkers. One of their big takeaways was that it not only made the code base easier to run through but it caught some subtle bugs that they didn't otherwise cover in unit tests. Does anyone know what this talk was? I thought it was about the requests library or something, but I can't find it.
|
# ? Mar 23, 2022 23:06 |
|
Thought someone would get a kick out of this terrible fstring I wrote today. Remind me how I get paid to write Python again?
|
# ? Mar 23, 2022 23:24 |
|
Protocol7 posted:Thought someone would get a kick out of this terrible fstring I wrote today. What the gently caress
|
# ? Mar 23, 2022 23:36 |
|
Protocol7 posted:Thought someone would get a kick out of this terrible fstring I wrote today. Apparently this works Python code:
|
# ? Mar 23, 2022 23:58 |
|
Armitag3 posted:Apparently this works https://peps.python.org/pep-0498/#specification it's in the spec, yeah. I think I had to use this recently.
|
# ? Mar 24, 2022 00:28 |
|
Today I learned that Python 3.10 supports match statements (i.e., switch, case, etc.), finally. Unrelated, but is there a function that slices a sequence given the starting index and length of the slice? I can write a function to convert (start, length) to (start, end) easy enough, but it seems like something that should be in itertools or elsewhere in the stdlib but I don't see it. ExcessBLarg! fucked around with this message at 02:00 on Mar 24, 2022 |
# ? Mar 24, 2022 01:50 |
|
I'm not aware of one, it's too easy to just slice as sequence[i:i+length]
|
# ? Mar 24, 2022 06:16 |
|
So the last issue I'm having with my project is that I'm trying to have the bottom label scrollable . Here is a snippet of my code that includes the "QScrollArea" and the label I want to apply it to "self.summary_screen". code:
So curious, I assigned the scroll area to a layout: code:
I just simply want the scroll window down at the bottom like self.summary.screen is. But it won't move no matter what I .setGeometry is set to. For the whole context, here is the full code: https://paste.pythondiscord.com/ocixuxamiv QuarkJets posted:It's still very difficult to diagnose what's going on here. What kind of object is Data? Why is self being passed to it? What does Data.run_api do? What invokes extract()? Is extract() part of a QDialog class or something else? There could be a lot of reasons that are causing you to see what you're seeing, basically. Places like stackoverflow have a rule that sums up as "post complete examples" for this reason, it's probably impossible to say exactly what's going on here using only what's shown. I tried messing around with this but I have so much code (since my project is all but done) that it would be easier implementing this for my next project. I'll keep tabs of this. Data is just a class btw. I posted my full code right above to get full context.
|
# ? Mar 24, 2022 06:26 |
|
punk rebel ecks posted:So the last issue I'm having with my project is that I'm trying to have the bottom label scrollable . This looks like a geometry issue. See here: https://doc.qt.io/qt-5/application-windows.html#window-geometry and the description of geometry here: https://doc.qt.io/qt-5/qwidget.html#geometry-prop, which is what's being manipulated when you use setGeometry(int, int, int, int) Basically, your other widgets are all given a parent widget: Python code:
Python code:
Returning to your earlier question, it seems like you should shove nearly all of the code in your extract() method into a new method that gets handled by a QThread or QThreadPool. But also, you're using a lot of repeated calls to a website to fetch the same data over and over: Python code:
|
# ? Mar 24, 2022 08:43 |
|
QuarkJets posted:I'm not aware of one, it's too easy to just slice as sequence[i:i+length]
|
# ? Mar 24, 2022 12:06 |
|
Today I learned that the Ellipsis ... in python is an actual object that can be used to mean I don't care
|
# ? Mar 24, 2022 12:28 |
|
"..." is a singleton object that I guess doesn't have much use in standard Python. So it's like None, but less meaningful. "pass" is probably a more idiomatic to indicate a "don't care" statement such as when dropping exceptions.
|
# ? Mar 24, 2022 14:42 |
|
I just refactored an entire module and it works correctly, 100% as expected the first time, what the gently caress? I don't think this has ever happened before. Ever.
|
# ? Mar 24, 2022 16:42 |
|
Anyone have any experience with Paramiko throwing an error during a rename? I can connect to the SFTP server and download a file from that remote host, but asking it to move the remote file (into remote archive path) results in an "[Errno 2] File not found". I can use the same client to lstat both the original filepath and the destination directory filepath and both return the appropriate information. I have full admin rights with the user on the SFTP-side. This is using an SFTP client object that's created using the .from_transport() method. The other commands I'm issuing with it (list_dir, file) work fine. There's not really much to it: Python code:
|
# ? Mar 24, 2022 17:15 |
|
Does it work if you issue a rename from a sftp command-line client? SFTP rename is weird. It's supposed to error out if the destination file already exists, which isn't how rename(2) works. Instead OpenSSH SFTP makes a new hardlink for the target and the unlinks the source. This requires that the underlying file system supports hardlinks, and specifically cross-directory ones if you're moving between directories. Not sure what SFTP server you're using but that it's on Windows makes this even more complicated.
|
# ? Mar 24, 2022 19:10 |
|
It’s Globalscape EFT, a relatively robust but persnickety hosting tool. I think it’s probably just going to be a no-go and I’ll just redo it from the SFTP-end and manipulate things at the OS. I’m genuinely curious is there’s anything that could be done to get it working but I’m not interested enough not to take the easy fix.
|
# ? Mar 24, 2022 19:28 |
|
QuarkJets posted:This looks like a geometry issue. See here: https://doc.qt.io/qt-5/application-windows.html#window-geometry and the description of geometry here: https://doc.qt.io/qt-5/qwidget.html#geometry-prop, which is what's being manipulated when you use setGeometry(int, int, int, int) You are correct. It works perfectly now. Thank you. QuarkJets posted:Returning to your earlier question, it seems like you should shove nearly all of the code in your extract() method into a new method that gets handled by a QThread or QThreadPool. But also, you're using a lot of repeated calls to a website to fetch the same data over and over: What do you mean by "cache this information somewhere"?
|
# ? Mar 24, 2022 20:39 |
|
punk rebel ecks posted:What do you mean by "cache this information somewhere"? Retain it in local memory somewhere, rather than fetching it each and every time. Your browser doesn't fetch the little bookmark star, or the images on the quote/report/reply buttons each time you need them. Rather, it caches them locally for faster lookup. EDIT: QuarkJets, thank you for the mapping trick. I use that extensively and it saves me a ton of if-elif-else statements. Cases in point: Python code:
EDIT EDIT: It's a really weird and good feeling to have your bugs being more dumb typos than just completely misunderstanding a concept. It's even better when those typos aren't in the method you're testing, but rather the method to call the test. Python is sticking with me like no other language has D34THROW fucked around with this message at 21:40 on Mar 24, 2022 |
# ? Mar 24, 2022 21:11 |
|
D34THROW posted:Retain it in local memory somewhere, rather than fetching it each and every time. Your browser doesn't fetch the little bookmark star, or the images on the quote/report/reply buttons each time you need them. Rather, it caches them locally for faster lookup. So extract everything from self.data and add it to a variable that I can sort though and pull from things that way?
|
# ? Mar 24, 2022 21:44 |
|
Phoneposting so no code, but if self.data is overwritten for each new loaded Pokemon, what I would do is store the self.data json (I'm assuming, or dict) in a dict cached_data, keyed by, say, the Pokemon number. Then I would add a clause to whatever function pulls the data to check first if that pokemons data is in cached_data, then pull from the API if it isn't.
|
# ? Mar 24, 2022 23:00 |
|
D34THROW posted:Phoneposting so no code, but if self.data is overwritten for each new loaded Pokemon, what I would do is store the self.data json (I'm assuming, or dict) in a dict cached_data, keyed by, say, the Pokemon number. Then I would add a clause to whatever function pulls the data to check first if that pokemons data is in cached_data, then pull from the API if it isn't. I’m not sure how to make a dict for cached-data.
|
# ? Mar 24, 2022 23:13 |
|
punk rebel ecks posted:I’m not sure how to make a dict for cached-data. I believe they're just using cached_data as a stand in for whatever you would want to name the dict. They just mean you should store it in a dict. You would make it similarly to how you would normally use a dict for caching, etc.
|
# ? Mar 24, 2022 23:33 |
|
Okay so I essentially just created this dictionary:code:
code:
code:
|
# ? Mar 25, 2022 04:37 |
|
I've never worked with Qt so I can't speak to the offload of work in QThreads or whatever, hopefully this gets the caching idea across, and may get you thinking about how you can modify your program's structure to make your life a little easier in the future:Python code:
The first time I call for Pikachu using from_cache, it's 100-200ms to retrieve from the API. The next time I ask for a Pikachu, the data is already local so I'm just spending the time to access the dictionary of Pokemon objects (absurdly fast in comparison, at the cost of a little bit of RAM). Python code:
code:
|
# ? Mar 25, 2022 07:00 |
|
nullfunction posted:
This is pretty much exactly what I had in mind. However, I am self taught and fairly new so classmethods are still fairly new to me. For my own edification, let me see if I understand here: Pokemon.from_cache returns a new instance of Pokemon (i.e. a constructor) if the Pokemon is not in the API, or the copy of the locally cached data. In other words, from_cache is a Pokemon factory? I had a pretty decent grasp of factory patterns and interfaces in VBA but for some reason, in Python they break my brain.
|
# ? Mar 25, 2022 15:00 |
|
D34THROW posted:Pokemon.from_cache returns a new instance of Pokemon (i.e. a constructor) if the Pokemon is not in the API, or the copy of the locally cached data. In other words, from_cache is a Pokemon factory? A classmethod is effectively a factory, really all it's doing is receiving the class as an argument, along with potentially other arguments, and then calling the constructor (or another classmethod that eventually calls the constructor). It's worth noting that it has access to class attributes, but not instance attributes: Python code:
E: since I was mocking this up pretty late at night, I didn't go into error handling, but in the case of a Pokemon not existing in the API, the code I posted above blows up. Were this code I was actually using, I'd be checking the status code of the response at a minimum, and raising a better exception (along the lines of a PokemonNotFoundError) and handling that error with something in the UI. In fact, it looks like that API only supports lowercase names, so doing a .lower() and .strip() on the name in the classmethod is probably a good idea too! nullfunction fucked around with this message at 19:13 on Mar 25, 2022 |
# ? Mar 25, 2022 19:01 |
|
|
# ? May 28, 2024 16:28 |
|
I can already think of an example (that involves major refactoring and thus is way beyond what I feel like loving with right now). I have a PGTExportData object that takes a path as an argument to its __init__ function. That path will be either to a CSV or an XML file, and I have a parse_xml function and a parse_csv function. Ideally, I'd make those classmethods for a PGTImport factory class and have them return a PGTExportData object rather than directly setting instance variables from within those two functions. Something like that? It blows my mind that the API goes by name and not number. Is it because of bullshit like Galarian Whateverthefuck and Kalosian Hoosiewhatsit?
|
# ? Mar 25, 2022 19:12 |