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
baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

There's another pattern I've seen (forget the name) where you have a list of handlers and you just fire your arguments at them until one of them goes 'yeah I've got this'. That way all the type checking goes on in the handler classes and only for the types they handle, and adding more cases just means writing a handler and adding it to your list

Feels a bit Java but I saw it on Python examples sooo :shrug:

Adbot
ADBOT LOVES YOU

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Yeah a lot of it's about knowing what BeautifulSoup can do and if it can make massaging your data easier, like maybe you can use getText(), that kind of thing

I'd say your 'take 10' bit should come at the end though. Get a pipeline going converting your data, and only pluck off what you need when you're going to do your average or whatever. Use generator comprehensions if you don't want to process more than you need

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

hooah posted:

The problem with doing that is there are elements that have the specified class which aren't numbers, so I can't convert all the elements first. I didn't know about generator comprehensions, though; I'll look into those.

You might want to do something like this

Python code:
import requests
from bs4 import BeautifulSoup

url = 'http://saws.org/your_water/aquifer/'
LEVEL_COLUMN = 4

response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# get the first table in the #main_content element (hella tables in here)
# then pull out all the rows, skipping the title row
# element.table is shorthand for element.find('table')
# element('tr') is shorthand for element.find_all('tr')
rows = soup.find(id='main_content').table('tr')[1:]
# for each row, grab the cell in the level column you're interested in
# this is a generator expression, doesn't create an intermediate list
cells = (row('td')[LEVEL_COLUMN] for row in rows)

# strip whitespace and the trailing unit character before converting to a float
parse_data = lambda cell: float(cell.getText(strip=True)[:-1])
data = [parse_data(cell) for cell in cells]

# select how many things you want when you're doing the calculation
for thing in data[:10]:
    print(thing)
It's a little less brittle, although that page isn't really helping things. The first bit is finding the actual table element and extracting the column of cells you want from it - they do have that class you're searching on, but that's a formatting thing and could easily change. They could still move things around but at least this is aiming for specific elements in a specific structure, so hopefully it would be easy to change. Doesn't fit the python yolo philosophy but I can't help it dammit

The other aspect is I used a few BeautifulSoup conventions to cut the code down - stuff like using property and method notation instead of typing 'find' and 'find_all' which cuts down on things and makes one-liners look nicer. I split out the actual 'convert cell contents into a number' bit into its own function, and the actual conversion step looks a bit nicer for it I think

I threw in a generator expression for the hell of it (no need to create temporary lists everywhere) but it doesn't really matter here, and I did a list in the end anyway because the slice notation reads nice and python doesn't have a nice clean take(10) function yet, so whatever. Generator functions are nice for a functional pipeline, you basically set up how sequences are handled and you can do filtering and stuff at each step, so for big data you can avoid putting everything in memory. Doesn't really matter here but it's good to know about!

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

You haven't closed your regex compile call on the previous line (missing the closing parenthesis)

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Line 25 just looked pretty ok to me so I figured the problem was caused before that! :frogbon:

Also your linter should really catch that kind of thing and put a red squiggle somewhere since it's never actually closed

baka kaba fucked around with this message at 19:36 on Sep 30, 2017

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

At a guess something screwy was going on in your Chunk class, so when you constructed a new one you were actually reusing an array (maybe handing the same one out multiple times so they all saw every append), or doing something with your pix list or the original Image object, something like that

Honestly in these situations the best thing is to run it through a debugger, set it to pause in a useful place so you can poke around at the current state of things, and you'll probably get an idea of where things are going wrong. Sometimes it's an obvious bug, other times it gives you a direction to investigate

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Cingulate posted:

"while" solutions usually feel rather un-pythonic to me. I think if you want to iterate over an iterable, you should iterate over the iterable!

Counting indices (or however that thing is called) are alien and un-pthonic.

Well guy hasn't been programming for a while! So long as it works that's the main thing, you can clean it up later

For this kind of thing I'd look at itertools, there are some functions that do chunking and windowing so you don't need to worry about janitoring your own ranges
oh wait now I remember, it does not have that and you have to write your own which is bad. Thanks python!

baka kaba fucked around with this message at 22:25 on Nov 29, 2017

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Noice! It would be cool if you could supply a pattern that says which chunks get glitched (so you can have some clean parts and maybe make the shapes everyone loves) or make the randomisation function get more pronounced as it goes, so it gets more glitchy as you go down the image

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Hughmoris posted:

How is python+selenium for filling out lots of repetitive forms? I noticed that some people on my project team are manually entering in the data for 2000+ users in to a web portal. They've asked for help but my eyes will fall out of my head if I have to manually type in crap.

I have all of the user data in a clean csv file. The steps that are needed are basically:
  • I log in to the web portal (just once)
  • Click on search field and enter user name
  • Click on said user
  • Fill in a couple of text boxes, check a couple of boxes, select values from a drop down list
  • save form
  • GOTO search for a user

I used AutoIT for a similar job a few years ago but I figured I'd give Python a try for this (plus I forgot AutoIT).

It is basically automating someone sitting at the computer and doing all that stuff though, probably take a while. Is it possible to use something like Requests and just POST the form data that's being sent, without having to load their web pages?

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Hughmoris posted:

Automation engineer sounds pretty cool.

No testing involved in this, just plain 'ole data entry. With my skill set, using pure POST requests seems pretty risky. I'll likely play it safe and use selenium to navigate the page while I surf the web.

Thanks for the ideas.

Honestly it's easy if it's possible - a lot easier than writing Selenium code (and all the edge case stuff to make sure the page is fully loaded, etc)

Here's how you set up a Session so all your requests can use the same login deets automatically
Here's how you POST some keys and values (spoiler: you just hand it a dictionary)
Here's how you can check the response status and make sure it worked

You can either look at the webpage to see what url the form is POSTing to, and all the key names for each of the inputs, or like someone said you can use Chrome's DevTools to spy on what the browser's POST request actually contains (just fill out your form, open the Network tab and empty it if necessary, then send your form and check out the request that pops up)

The main issue I can see is that your form might be sending a user ID or some other identifying token, and maybe the only way you can get that is to search for their name? You might be able to do that with requests too... but yeah, if you can just do your own HTTP it'll be way faster and simpler. But if you also want to learn Selenium, this is a good way to do it!

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

These are important if you don't know 'em:


https://www.youtube.com/watch?v=XOkNJxvNtPw
Code completion (ctrl+space), using snippets and templates, and quick fixes (alt+enter) instead of typing all the dang code, and stuff like auto formatting



https://www.youtube.com/watch?v=jmTo5xTRka8
These are huge: turn off your tabs, hide the tab bar because you don't need it, preferably put away your mouse for a bit, gotta go fast

  • Double-tap shift to find anything anywhere
  • Ctrl+shift+a to find any IDE action or preference (sometimes there's a toggle right in the popup)
  • Ctrl+e pops up a recent files menu (here's your tabs)
  • Ctrl+tab does the same only it opens the selected one as soon as you let go of ctrl. Hit tab more times to move down the list, hold shift to go back, you get it
  • There's more specific searching stuff in the video


In the help menu there should be a 1-page pdf of keyboard shortcuts - have a look at that, because it lists a bunch of cool features that make you go 'wait... is that...?' and then you find out it is. I forget if pycharm has it, but there should also be Productivity thing in help too, that shows you features you use a lot and how much typing they've saved you, but also lists stuff you might not know about. And there's the tips popup too of course


I don't know exactly how much crossover there is with IntelliJ, but this is a good talk that's old but entertaining and most of it should carry over. Get you feeling extremely powerful. Covers cool things including double-tap ctrl to add extra carets with the arrow keys (what, you work on one line at a time? :smugdog:) and uhhh alt+backspace or something to jump to the last edit position, all kinds of stuff

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

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Now that's irony

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

You can also do

Python code:

from itertools import product, chain

combos = (product(d1[server], d2[server]) for server in d1.keys())
as_strings = ["".join(parts) for parts in chain.from_iterable(combos)]

which gives you all the name/extension combos for each server. It's an iterable representing each server, each item is an iterable of all that server's combos, held in tuples. So you can use chain to unpack all those nested iterables and just spit out one sequence of tuples, and join 'em up however you like

(also it reads nicer if you change "d1" to "names" and "d2" to "extensions" or whatever!)

baka kaba fucked around with this message at 00:09 on Dec 22, 2017

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Oh I meant in mine really, just that attribute[server] will read more clearly, so it's obvious what you're getting the product of

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

VikingofRock posted:

Why does this happen? (Python 3.6.3)

Python code:
letters = {'a': 1.0, 'b': 2.0, 'c': 3.0}
fns = {key: lambda x: x / val for key, val in letters.items()}
for key in fns:
    print(key, fns[key](1))

# output:
# 
# a 0.3333333333333333
# b 0.3333333333333333
# c 0.3333333333333333

http://quickinsights.io/python/python-closures-and-late-binding/

Yr lambdas are getting defined, with val as a reference to the local variable in your loop (not inlined as a constant), and when you run the lambdas they all look up the value of val. The loop has already finished running when you call them, so in each case val is set to the last value it was assigned during the loop, i.e. 3.0.

baka kaba fucked around with this message at 00:14 on Dec 27, 2017

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Assuming you're importing Ex45 into Engine.py too (since you're calling Intro() and that class is defined in the other file), you've got a circular dependency - both modules depend on each other

There's an explanation here with examples - the problem is you start to load file A, and it gets to the import statement and has to load file B. File B wants to import A - and that's fine, it's happened already, but it hasn't finished loading A yet. So you get to the line where it tries to use something from file A, Python hasn't seen that function or class yet (it doesn't exist as far as it's concerned), so it throws an error

At this point you're probably thinking ugh this is bad and complicated and why did I bother - and yeah, often it's better to keep things in the same file. Or you have to organise the project so you don't get circular dependencies. If it's unavoidable, a lot of people recommend putting your imports at the end of the file (so they only run after the file has been fully loaded)

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

A loop... loops! It can go around multiple times, until some condition is met

The if/elif stuff is just a conditional - you go through it once, and follow the first branch that matches, then you pop out the other side

You can put a conditional inside a loop of course, which is pretty common - iterating over several things, doing something different based on the current situation each time around

baka kaba fucked around with this message at 02:08 on Jan 23, 2018

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

If it helps, variables in python are basically just keys in a dictionary, so you can just go thing = whatever at any time and it just stores that key/value pair. So there's no need to pre-declare that variable like you would in other languages where you want to reuse the same reference - you're just overwriting the same entry in the dictionary

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

The easiest thing would be to add the numbers as you do the counting - I'm guessing you're splitting the sentence on whitespace, and doing something smart about punctuation, so that would be a good time to decide where the count should go (like it needs to go before that colon in the first sentence, but apostrophes would be treated differently etc). So you'd take each word, count it, then transform the string to add the count, and eventually just use join to put it all back together

If you're in the situation where the counts have already been done, you just need to get back to that state again - split each sentence into words. Then you'll have two structures that mirror each other (a list of lists of words, and a list of lists of counts), and you can iterate over those together, using zip to get pairs of word lists and count lists, and using zip again on those to get pairs of words and counts

tldr you're probably looking for zip

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

If you look at the error, it's saying you can't do str() <= int(), and there's something that looks like that in the line it quoted

you're casting to int after you've done the comparison - you need to get used to the order it's written in python, but it does the for bit to get items, then the if bit to filter them, and then it does the bit on the left with the results.

You can think of the for part as item from items (where item passes this test)

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Yeah ideally you'd convert all that as you parse it. Read in each line, split it and convert each item to an int. That way your "get input" part hands you a pristine set of int lists, which is the structure you want to work with

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

wasey posted:


How do I assign each line element to an object in Python, e.g. first task ID = 1, start time = 1, finish time = 4? I created an activity class
code:
class Activity():
	def __init__(self, id=None, start=None, finish=None):
		self.id = id
		self.start = start
		self.finish = finish
but I can't seem to properly assign the info. Thanks for helping out

You need to work out your plan first. Basically (if I'm reading this right) what you want to do is:
  • read in a line
  • assume it's a single number that represents a line count, so parse that as an int
  • read and parse the next count lines as Activities
  • repeat this whole thing until you run out of lines

and the activity parsing bit goes
  • read in a line
  • assume it represents an activity, i.e. three numbers separated by spaces
  • split and parse that line as three ints
  • do something with that data

hopefully that makes it a bit clearer it's the "do something with that data" where you need to store your activities
You might not want to use classes, but only because it's not 'pythonic' - there's nothing wrong with using them while you get your head around the problem. It's just there are simpler tools you can use that will do the same job here (better, even)

edit- it might help to explain exactly what you want to have at the end of this. How should it be organised? Do you need to store the 11 and 3, what do they represent?

baka kaba fucked around with this message at 19:24 on Feb 3, 2018

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

I think you'll have to post your code to know if there's a more elegant way, but I don't think Python really encourages anything besides the ol' for loops in this case

Some languages have some kind of window function, which you could use to produce a sequence of each element and its successor (so in your case (0, 14), (14, 18), (18, 20) etc). That way you get a stream of items and the thing you need to compare them to, and you can start to group them based on whether they meet your rule or not. Python doesn't have this though, for some reason

It does have a reduce function but again, I'm not sure I'd exactly call it elegant here - it's definitely not as Pythonic (i.e. readable to general Python users) as a bunch of loops and conditionals doing the same thing

Python code:
from functools import reduce

def group_close_items(groups, item):
  # groups is a list of lists - we get the last (most recent) list, compare to
  # the last item, and either add our new item to that list if it's close enough
  # or start a new list for a new group if it's not
  last_group = groups[-1]
  if item - last_group[-1] <= 5:
    last_group.append(item)
  else:
    groups.append([item])
  return groups # with a reduce function you always return the new state
  
items = [0, 14, 18, 20, 36, 41, 62, 70, 72]
# we need to create the initial state for the reduce function, so it has something
# to compare items with - so we stick the first item in its own group, and use the
# rest of the items as the iterable we're reducing
groups = reduce(group_close_items, items[1:], [[items[0]]])
# looks like you're dropping any groups with a single item, so here ya go
no_singles = filter(lambda x: len(x)>1, groups)
print(no_singles)
you could probably clean that up a bit but it's still probably more awkward than what you've got. You could mess around with other stuff too but it seems like overkill for what you're doing

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

The more pythony way might be to have a generator that yields lists (basically consumes the iterator, adding each item to a list it's building until the item is too big, yields the old list and creates a new one for the item). That way you can feed that into another generator that filters out the single lists, or have the first generator just not yield a single item list

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

I was thinking more like
Python code:
def group_em(items, max_diff):
  group = []
  for item in items:
    # if the group's empty, or if the item is close enough to the
    # last item in the group, stick this new one in there
    # otherwise spit out the group and start a new one with the item
    if not group or item - group[-1] <= max_diff:
      group.append(item)
    else:
      yield group
      group = [item]
  # when there's nothing left, produce the current group
  # (it'll only be empty if the input list was empty)
  yield group

items = [0, 14, 18, 20, 36, 41, 62, 70, 72]
grouped = group_em(items, 5)
# this is the same as filter(lambda x: len(x) > 1, grouped)
big_groups = (group for group in grouped if len(group) > 1)
print(list(big_groups))
I think it's clearer and more readable if you're just working with the lists you're going to return instead of messing around with indexes - you're just building a list, until you hit an item that you can't put in it, so you shove your list out the door and start a new one for the item. When you run out of items you send out that list and then you're done

You can do a length check before you yield (so you only get lists with 2 or more items), but there's two problems - one is that you have to put that code in there twice (you're yielding in two different places), and it also ties that implementation detail into the grouping function. It doesn't just split your list into groups where each item is close to the last, it also does another job of removing lists below a certain size. Personally I'd argue those are two different things (you might not even want the filtering all the time, or you might want to change the size limit), so it makes sense to do that separately, running it on the results of the grouping function

The nice thing about generators it's they're on-demand, so you can put them together like a pipeline. Instead of producing a full list of groups, and then producing another list where some of those groups are missing, that code up there iterates over big_groups, asking it to produce a single group each time. (big_groups is a generator comprehension, like a list comprehension but in ( ) so it produces a generator.) So in turn, big_groups asks grouped for an item, which it yields when it's built one. If big_groups discards it (because it doesn't meet the 'big enough' condition) then it asks for another one, until it has something it wants to yield

That doesn't necessarily matter, but you can see how you can set up a big chain that yielded items have to pass through, getting tweaked or dropped or whatever, creating a machine that produces single, finished items on demand. And that can be good for working with very large amounts of data, or something slow where you want to drop things as early as possible if you're not going to be using them later in the pipeline

baka kaba fucked around with this message at 02:36 on Feb 9, 2018

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Wallet posted:

I take your point about them being different functions, though in this case there isn't really a context where you'd want length 1 groups.

You can do the filtering without duplicating the code twice with a little tweaking (only one extra line of code, I think?):

Sure, that works! I feel like you're introducing a lot of complexity just to have a single yield statement in there though, and it's less clear what job it's actually doing. Personal preference obviously

And the thing with putting the filtering outside... I guess it's more of a functional approach, where you're encouraged to write smaller, simpler single-task functions and then compose them together - not necessarily because you're gonna use them in isolation too, just that it's really readable when you look at each one or the code that chains them together

It's great for writing pipeliney stuff, especially if you name your functions well, because you can see exactly what it's doing as a set of steps - like I had to infer you were dropping those groups by the example result you posted, but adding a line that filters them out makes it explicit, and you don't need to rely on comments either. Personal preference again of course!

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

If that's not clear, instead of replacing each string in the list with a new "great" version, you're just creating a new list by taking elements from the original, doing something with them and sticking the results in your new list. Like

Python code:
greats = []
for magician in magicians:
    greats.append("The Great " + magician)
return greats
so like you're doing now, but you don't need to bother with indices or lengths or anything. That one-line list comprehension version is a nice compact version of the above code, but that's basically what it's doing

Also this is a little advanced, but you can print all your magicians in one line:
Python code:
print(*magicians, sep='\n')
basically you can print more than one object by calling print(thing1, thing2, thing3...) - trouble is you have one thing, a list, and if you print that you'll just get a representation of that list structure. But if you add the * star in front of it, it basically explodes the iterable into a sequence of objects, so you get magician1, magician2 and so on. (Try it with and without the star and you'll see). The sep keyword argument is the separator for the items you're printing, you want each on its own line so we're separating with a newline character

Not saying this is better in this case, but it's good to know!

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Another way you can look at it is, usually you do for thing in stuff to get a sequence of items, easy. If for whatever reason you want to number those items too, just call enumerate and it'll handle the counter and pass you the number with each item. The work's already done for you, there's less chance of making a mistake, it's clear what's happening, and if you don't unpack it as count, item then you have a tuple ready to pass into something else, you don't need to build it yourself

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Slimchandi posted:

When I was learning Python basics, it took me a long time to realise that when using a list comprehension, you can use any name in the for part.

I saw examples of [letter.lower() for letter in name] and assumed you hadto use letter if you were iterating through a string. :(

I always try to avoid doing callable/in-place stuff in a list comprehension eg [print(letter) for letter in name]. Is there any particular reason to avoid this? It seems to be more concise than the indentation of a for-loop.

As well as what people have said, print takes varargs anyway, so you can unpack an iterable like my example a couple of posts ago

Python code:
print(*[letter for letter in name])
that way you are generating a list, and then you're passing it to print so it can print each element, so the list is actually getting consumed and it makes sense

it might make more sense to use a generator comprehension instead though, since you don't really need the list
Python code:
print(*(letter for letter in name))
pipes each letter direct to the print function, feels good!

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Cingulate posted:

In this case,
code:
print(*name)
will do the same though.

Doh yes

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

I guess there isn't really a use for it since you can do *name, especially with the Python philosophy of having one way to do a thing. It would be more convenient if the separator was supplied from elsewhere, so you always get consistent behaviour, but hey


Slimchandi posted:

For clarity, I was referring to functions with side effects or return None rather than just callables. The given example was exactly what I intended.

It seems much more convenient than a for loop; is it just non-conventional or A Bad Thing?

It's convenient in the sense that a 2-line (or 3 with a conditional) for loop can be put on 1 line instead, or even more complicated nesting. It gets to a point where that might be less clear about what you're doing though

The problem is that list comprehensions generate a list, that's their whole purpose. When someone reads your code, and they see a list comprehension, they see you generating data, they don't see the side effects. You can argue that the clues are all there because a) you're not keeping the list and b) it's calling print() and everyone knows what that does, but that's not always necessarily true. The actual purpose of your code is implicit instead of explicit. (You're also actually generating a list full of Nones, which is grody garbage at best and might actually cause memory issues at worst)

Unfortunately Python doesn't seem to have any equivalent to forEach, where you'd have something like for_each(function, iterable) and it would explicitly call the function with each element, returning nothing. There's a consume recipe in itertools but that means it's not actually in the standard library, for reasons, and you'd have to put it in your own code somewhere. And that means you need to add your own iterable of function calls, like consume(print(c) for c in name) or consume(map(print, name)). Which is better, in that it's more explicit, but I think the plain for loop is more pythonic

If you do it a lot though, no reason you can't write your own utility function to make it super explicit and concise!

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

^^^ that's what I'm saying, in most cases that's probably the best way to do it

Yeah I used map in my last example - it returns an iterable though, you still have to consume it by iterating over it somehow

Personally I don't really like the semantics of that anyway - map is for transforming elements from one set of data into another set of data, so it produces some values. In this case you're still ignoring the results and relying on the side effects of the mapping function - like here's one way to consume that example I gave:
Python code:
for _ in map(print, "hallo"):
  pass
I mean it works, but it sucks. Functional languages tend to have functions that work like map (take a mapping function and a sequence of elements to run it on), but that explicitly don't return a value (or return unit, or whatever you want to call it). forEach, iter, they just immediately do the thing to each element and you know it's explicitly about the side effects

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

That's some sweet reduce action

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

map is for transforming a bunch of things (it maps values to other values, basically). So if you have a set of elements, but you want to change the NAs to 0s, your mapping function wants to output a 0 if it gets one of those, and just pass everything else through as-is (or map the value to itself, if you like)

Python code:

lambda x: '0' if x == 'NA' else x

that way the sequence of elements you get out is the same as what you put in - you've just replaced some of them with different values.

What your code does is filter out all the non-NA lines (right?), leaving you with only a bunch of NAs, and then you turn those into a bunch of 0s instead - which isn't very useful! You want to keep all the elements, but use map to selectively change some as they pass through

Using a regex find and replace might be a lot better anyway, I just wanted to point out what the functional stuff is about

baka kaba fucked around with this message at 02:43 on Feb 26, 2018

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Probably worth benchmarking it (Python's lists are apparently arrays, so a reversed iterator should be fast?) but you could always just iterate over the array normally, assign the index to a variable whenever you find a match, and then at the end it'll be set to the index of the last matching element (or None)

Array traversal should be fast either way, not sure about Python's implementation - reverse iteration is neater though (since it stops as early as possible)

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

The loop protects you from the universe's ironic sense of humour

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Sad Panda posted:

Next part of my Blackjack program. A lookup table. A short extract of the data would be...


code:
        2   3   4   5   6   7   8   9   T   A
Hard 5  H   H   H   H   H   H   H   H   H   H
Hard 6  H   H   H   H   H   H   H   H   H   H
Hard 7  H   H   H   H   H   H   H   H   H   H
Hard 8  H   H   H   H   H   H   H   H   H   H
Hard 9  H   D   D   D   D   H   H   H   H   H
Hard 10 D   D   D   D   D   D   D   D   H   H
I want to be able to input 2, Hard 6 and it return H.

My original idea was 2D arrays, but that doesn't seem to support a column name which is what I'd call that 2/3/4/.. at the top. I found one solution, and he used a Pickled 'av table' (so the variable name suggests), but that seems a bit beyond me right now.


You could make each row a namedtuple type with a parameter for each column name

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Sad Panda posted:

Python code:
def bubble_sort(list):
    print(f"Original list is {list}")
    consecutive = 0
    attempts = 0
    # Check each item, if list[i] > list[i+1] swap them else leave them
    temp = 0
    for j in range(0, len(list)):
        for i in range(0, len(list)-1):
            attempts += 1
            print(i)
            if list[i] > list[i+1]:
                temp = list[i+1]
                list[i+1] = list[i]
                list[i] = temp
                print(f"Need to swap {list[i]} and {list[i+1]}")
                print(list)
                consecutive = 0
            else:
                print("Don't need to swap")
                consecutive += 1
        print(f"End of pass {j}")
        if consecutive >= len(list):
            print("A whole pass has happened with no swaps")
            break
    print(f"After {attempts} attempts...")
    print(list)
    return list

Not that it really matters, but the way you're checking if no swaps happened can cause an extra pass to occur. In a list of n items you're doing n-1 comparisons each pass, but you're looking for a chain of n or more comparisons that didn't cause a swap. So with a list like 5,1,2,3,4 the 5 will bubble up on the first pass, and now the list is sorted. But you want 5 swapless comparisons, and so far you have 0, so you do another pass, and now you have a run of 4. Gotta do it another time! Now you have 8

You could change the comparison operator, but really you can do this all a lot simpler - on each pass, set a 'swapped' flag to false. If you do a swap, set it to true. At the end of the pass, if swapped is still false, you're done!

You can also do smarter stuff with the looping, based on the idea that the largest item always bubbles to the right so the list gradually gets sorted from right to left (so you don't need to check those end items anymore) but bubble sort isn't very efficient anyway - that's probably why it seems slow, it is!



While we're on this, this is the best one of those visualisation videos I've seen

https://www.youtube.com/watch?v=sYd_-pAfbBw

The hue is where the dot should be in the array, and the closeness to the centre is how out of position it is. As each one gets sorted it flies out to its position on the circle edge. Some wild stuff happens in there :eyepop:

baka kaba fucked around with this message at 17:45 on Mar 18, 2018

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Assuming PyCharm has the same basic debugging capabilities as IntelliJ, you can add breakpoints wherever, then right-click them and uncheck pause execution (or whatever), and add your own print statement in the "execute this" bit - you can include variables and all that. It's like adding print statements without sticking them in the code

But stepping through and watching what happens is your best bet

Adbot
ADBOT LOVES YOU

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

My fav thing is being able to reach in and change the value of variables (even by executing some arbitrary code) and then set it running again

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