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
Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

QuarkJets posted:

Most linux flavors ship with a system python (and make a ton of python packages available via yum or apt) for exactly this reason. Python is pretty handy and has a lot of cool stuff built from it, so it's reasonable to make that stuff available from a standard system location for multiple users to use. So if you have some Python package that you want to make available to your users, especially users who aren't doing their own development, putting it in the system location is a-okay in my opinion.

I too am flummoxed when I come situations where dicts are being passed around. It's not ideal, even when the dict is documented someplace (and let's face it, the documentation will wind up being full of lies at some point, if not immediately). It works fine enough obviously, you can robustly handle missing keys and whatnot, but it just feels a lot more fragile and harder to maintain.

We were doing a cache stored as json and handled as a dict, and I just finished refactoring in an abstraction layer and got to delete so much weird code that was all around handling these weird fragile dictionaries, and bonus, it's gonna make it much easier to switch that over to a proper storage system like DynamoDB.

Falcon2001 fucked around with this message at 07:30 on Apr 14, 2022

Adbot
ADBOT LOVES YOU

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:

QuarkJets posted:

Each module has its own scope. When you import from one module to another, those values are copied to the new scope. Altering that new variable, even via globals(), will not alter the variables that exist in another module's scope.

Even if you could do this, you absolutely shouldn't anyway; this kind of design is incredibly frustrating to try and debug when problems occur. I don't know what the examples you're seeing look like, but if they're doing things like this then you should look for new tutorials.

Why do you need a global user_datastore variable? Just pass it around to whatever objects need it. Stick it in a class and pass that around instead, if you want

I hacked around it for now by doing a from app_data import user_datastore at the top of any function that used it. My next step is to stick the fucker in a class and deal with it that way.

EDIT: It's working and after an hour of loving around trying to find out why it wasn't finding my index route (app.register_blueprint(bp_auth); app.register_blueprint(bp_main) != app.register_blueprint(bp_auth); app.register_blueprint(bp_auth)) serving pages :woop:

D34THROW fucked around with this message at 15:29 on Apr 14, 2022

death cob for cutie
Dec 30, 2006

dwarves won't delve no more
too much splatting down on Zot:4

QuarkJets posted:

It's bad practice, yes. There's virtually no good reason to do this. Classes should be well defined, and defined once.

this is what I was trying to convey to my coworker, but worded much more nicely - thanks

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

Falcon2001 posted:

dataclass talk

Actually, I should expand on this in case anyone is newer and reading this, like I was not that long ago.

So it is extra code and extra work to define data storage classes, so there is a tradeoff here. But what I'm poking at here is the idea that a lot of times you use a dictionary because it's convenient, because putting things into a dict and taking them out is a reasonably easy operation. Especially with certain external operations it's easy to just go 'oh yeah, json turns to dict, I'll just keep it.' and that's not always a terrible idea. If the json schema is well defined, it's probably fine; you'll just need to look up the schema later. But the bigger problem is when you start making your own dictionaries to hold data.

However, the tradeoff is that new development turns into a constant fight of 'wait what the gently caress is in here. What are the keys? Let me run a debugger. OH okay that's what I want.', which isn't the case if you define a class, because your IDE is going to tell you the attributes of the class readily, and even if you don't use an IDE, you're one hop away from just looking up the code yourself.

(This part is like CS101 but putting it out there as an example) The other upside to using your own classes for data structures is that it gives you all the OOP benefits of classes so it can be as lightweight or heavy as you want it to be. For example, I want to cutover from a json flat-file for storing our cached data to DynamoDB or another lightweight database soon, and by adding an abstraction layer to this, I was able to ensure that the rest of my code is not going to need significant overhauls in the future, since instead of dictionary style syntax, we're using more general syntax, and some helper methods. Whenever we cutover to a new storage system I can just update the new cache_types I defined and keep all my changes pretty confined.

That all said, there are some downsides, namely that it is more code to write, but like most coding, it's one of those tradeoffs between today vs tomorrow.

ExcessBLarg!
Sep 1, 2001
I'm not sure I'm into "dataclasses". Either have a class with data and methods defined on it that do what you need to do with it, or write your functions to take five keyword arguments and shove your **kwargs in a dict.

Edit: To be clear, if you have modeled data that follows a schema that you're pulling in from a database and passing around to various controller methods, sure that makes sense. But I wouldn't make a class just for the sake of holding common (keyword) arguments to an external set of functions.

ExcessBLarg! fucked around with this message at 02:00 on Apr 15, 2022

12 rats tied together
Sep 7, 2006

Why not? That seems like fairly fundamental OOP to me.

I've worked on codebases where oops, the first kwarg of the most core behavior of the app was based on an assumption that stopped being true after 11 years, and now I get to push a PR that changes the signature of many dozens of functions across the entire codebase.

Would have been way easier if we were already accepting OurThing instances instead of what is essentially an unpacked version of OurThing in random scopes with no guarantee that they haven't been mutated in some way.

ExcessBLarg!
Sep 1, 2001
It's a common trap to have an Everything class that contains attributes for everything and gets passed around to every function to pick the parts they care about.

It's hard to comment without specific examples. I'm just suggesting if you feel you need to pass "something" around to a handful of external functions, and that something is not clearly modeled data, consider if **kwargs is a more appropriate alternative.

QuarkJets
Sep 8, 2008

ExcessBLarg! posted:

I'm not sure I'm into "dataclasses". Either have a class with data and methods defined on it that do what you need to do with it, or write your functions to take five keyword arguments and shove your **kwargs in a dict.

Edit: To be clear, if you have modeled data that follows a schema that you're pulling in from a database and passing around to various controller methods, sure that makes sense. But I wouldn't make a class just for the sake of holding common (keyword) arguments to an external set of functions.

I take it that you just haven't used them much? You can add methods to a dataclass just like you would with any other class. And they very easily convert to dict in case you need that, such as if you need the fields in your class to function keyword arguments. So I ask whether you've used them much because they seem to provide everything that you've asked for in your post

ExcessBLarg!
Sep 1, 2001
I haven't, hence "I'm not sure". Being able to convert to dict to use as kwargs seems rather convenient.

QuarkJets
Sep 8, 2008

ExcessBLarg! posted:

It's a common trap to have an Everything class that contains attributes for everything and gets passed around to every function to pick the parts they care about.

It's hard to comment without specific examples. I'm just suggesting if you feel you need to pass "something" around to a handful of external functions, and that something is not clearly modeled data, consider if **kwargs is a more appropriate alternative.

Sure, I agree with all of that.

But your previous post was kind of against the core premise; there's a lot of room between "use one dataclass that holds everything that any function might need" and "don't use dataclasses". Also, the specific post where a dataclass was recommended contained clearly modeled data (animal demographics), and the recommendation was to just wrap that data in a dataclass and not the misc. other function arguments. That's a good usecase.

I personally use/recommend dataclass arguments sparingly; only when it makes sense to group together arguments, and only when you're going to need to reuse those same arguments in multiple places. Then a dataclass is handy. But like any other class, you shouldn't create one without a reason.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

QuarkJets posted:

Sure, I agree with all of that.

But your previous post was kind of against the core premise; there's a lot of room between "use one dataclass that holds everything that any function might need" and "don't use dataclasses". Also, the specific post where a dataclass was recommended contained clearly modeled data (animal demographics), and the recommendation was to just wrap that data in a dataclass and not the misc. other function arguments. That's a good usecase.

I personally use/recommend dataclass arguments sparingly; only when it makes sense to group together arguments, and only when you're going to need to reuse those same arguments in multiple places. Then a dataclass is handy. But like any other class, you shouldn't create one without a reason.

FWIW as the recent dataclass lover, I agree with all this, because mostly the idea is 'if you're going to be passing around data from place to place as a clear set of data, you might want to schematize it'. I'm mostly arguing against dictionaries, not against overuse of classes/etc. If you're just passing info from one place to another, then yeah, just pass them as arguments.

For example, the 'responses' dictionary I was complaining about is data from a specific process - it's not going to change regularly (or if it does, it's going to gently caress up our schema enough for a code change anyway), and we basically pass it along a pipeline into multiple places. By the time I'm into the fifth step, there's no easy way for me to go find where the heck it's built anymore without walking back the code a bunch.

ExcessBLarg! posted:

It's a common trap to have an Everything class that contains attributes for everything and gets passed around to every function to pick the parts they care about.

Yeah also this would definitely be bad and I agree with you...except how much worse would it be if it was a dictionary instead.

Falcon2001 fucked around with this message at 04:56 on Apr 15, 2022

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I needed to come to a very pedantic understanding of global, nonlocal, and local in Python and I just wanted to share with you a code snippet that I think you will absolutely not enjoy:

code:
a = -1
def f1():
  a = 2
  def f2():
    def f3():
      global a
      a = 10000
      def f4():
        a = 5
        return a
      a += f4()
      return a
    
    nonlocal a
    a -= f3()
    return a
  a += f2()
  return a
a += f1()
print(a)

SurgicalOntologist
Jun 17, 2004

I prefer dataclasses myself, but I felt like I should add to this conversation that TypedDict exists.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Is there a good dummies' guide to unit testing? It's something I want to explore - though many of my calculation classes output dicts, etc., there are other cases where I may want to do automated unit testing. RealPython's tutorial is okay, but for some reason I'm having a really difficult time even understanding unit testing.

Pie Colony
Dec 8, 2006
I AM SUCH A FUCKUP THAT I CAN'T EVEN POST IN AN E/N THREAD I STARTED
Have you seen the standard library docs on unit testing? https://docs.python.org/3/library/unittest.html

At a high level, "testing" is automated verification and "unit" is the smallest abstraction you have. Depending how you like to write code, a "unit" could be a single function, a class method, or even an entire class. In any big enough project no one really makes sure every unit test is testing a unit, but it's a good practice.

There are other layers of testing beyond unit tests -- see what's called the testing pyramid. Testing how "units" compose/interact with each other in a single program is called "integration testing," and testing how entire programs interact with each other is called "component testing" for instance. The reason it's a pyramid is because most of your tests should be unit tests though (intuitively, you should have more units than components, etc).

You can and should absolutely test dictionary contents from your functions. But as the posters above have said, schematizing your dictionaries into dataclasses/TypedDicts is even better (type checking is another form of testing!)

Pie Colony
Dec 8, 2006
I AM SUCH A FUCKUP THAT I CAN'T EVEN POST IN AN E/N THREAD I STARTED
Some more words cause I'm blocked at work right now--

You don't actually have to focus on those other types of testing right now, unit testing is a perfect place to start. A lot of projects don't even have any other kinds of tests (although they usually do test "integrations" in the same unit testing framework).

Basically what your unit test should aim to do is verify that, given some inputs, your unit does the expected thing -- usually return a specific value, but it could also return nothing and just have a side-effect. It's usually a lot easier to test when it just returns a specific value. Imagine you have an addition function, you could easily write a test that confirms `add(2, 2) == 4`.

Side-effects are a bit harder to test because they admit the possibility of a world outside the unit. For instance, maybe instead you have a `register_account` function that persists data to a database (this is already venturing into "integration" testing but I digress). To actually test that function, i.e. the logic you wrote inside of it, and NOT the 3rd party database code you're using, you have to mock/patch out the database call (see the `unittest.mock` standard library) so that it goes to a test object you can inspect -- e.g. was this database call made with the correct computed value?

All of that stuff is kinda messy, so a lot of people prefer to write pure/functional/side-effect-free code that just allows them to test inputs and outputs. It makes testing a lot easier and usually leads to a better architecture in your own code.

However, we write programs because we want the side-effects. So you should be testing how your units interact at some point. For instance, you may think everything's fine because your test of `register_account` (with a mocked out `write_to_db` function) passes, but maybe in the real world that function can't even connect to the database because of some other issue. This is where the higher-level testing comes into play. In the specific example of databases, you could do anything from create (and tear down) an in-memory version of your database, create (and tear down) a local instance of your database, or use some sort of shared test/staging environment database -- none of which would be unit tests, but could still help you verify your program's working correctly.

QuarkJets
Sep 8, 2008

I like pytest

Data Graham
Dec 28, 2009

📈📊🍪😋



Protip from someone who is in the unit testing weeds right now:

Write very small, atomic, reusable methods, and lots of them. Don't write enormous monolithic multi-indented functions that go on for hundreds of lines and can only be tested completely end-to-end by mocking fifty different API calls and context managers.

Unit testing allows you to get code coverage of all your small methods, and then just put those small methods together into one big lego sculpture that inherently also has pretty good code coverage even though it requires access to things that are an enormous pain in the rear end to mock.


*fumes at the guy who wrote this loving thing good god*

Macichne Leainig
Jul 26, 2012

by VG
I have a 5000 SLOC Python file that's comprised of about 15 or so functions from our offshore team so I feel your pain.

The worst part is, it's a file I had set aside for like a dozen configurable variables to live in one place, and they completely misused the entire file.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
I was gonna ask a question here and then realized that the issue was that I was using round() and expecting 2 digits after the decimal, but failed to specify ndigits=2 :doh:

Pytest is awesome and it gave me the idea to center my startup line on the terminal which then spawned a function to do so. Nice little project for today, getting this all running somewhat.

SmashingNine
Jun 7, 2007
I'm an idiot and didn't know that pyinstaller has a --uac-admin option.

SmashingNine fucked around with this message at 16:55 on Apr 19, 2022

Mycroft Holmes
Mar 26, 2010

by Azathoth
I'm working on an assignment for class where I have to scrape a table from wikipedia, put it in a dataframe, display it, then write it to a csv. I am having trouble displaying it. Some of the rows have a null value, and my cod is repeatedly giving a NoneType error. I have attempted to put in an if, else statement that will replace the null value with "Blank." This is replacing all my values with blank. Here is my code and the error:

code:
A = []
B = []
C = []
D = []
E = []
F = []
G = []
H = []
I = []
for row in right_table.find_all('tr'):
    cells = row.find_all('td')
    if len(cells) == 9:
        country_link = cells[0].find_all('a')
        if cells[0].find(text=True) == None :
            A.append("Blank")
        else:
            A.append(country_link[0].contents[0].strip())
        if cells[1].find(text=True) == None :
            B.append("Blank")
        else:
            B.append(cells[1].find(text=True).strip())
        if cells[2].find(text=True) == None :
            C.append("Blank")
        else:
            C.append(cells[2].find(text=True).strip())
        if cells[3].find(text=True) == None :
            D.append("Blank")
        else:
            D.append(cells[3].find(text=True).strip())
        if cells[4].find(text=True) == None :
            E.append("Blank")
        else:
            E.append(cells[4].find(text=True).strip())
        if cells[5].find(text=True) == None :
            F.append("Blank")
        else:
            F.append(cells[5].find(text=True).strip())
        if cells[6].find(text=True) == None :
            G.append("Blank")
        else:
            G.append(cells[6].find(text=True).strip())
        if cells[7].find(text=True) == None :
            H.append("Blank")
        else:
            H.append(cells[7].find(text=True).strip())
        if cells[8].find(text=True) == None :
            I.append("Blank")
        else:
            I.append(cells[8].find(text=True).strip())
import pandas as pd
df = pd.DataFrame()
df['Country'] = A
df['Total Population 1/1/1939'] = B
df['Military deaths from all causes'] = C
df['Civilian deaths from military actions and crimes against humanity'] = D
df['Civilian deaths from war-related famine and disease'] = E
df['Total Deaths'] = F
df['Deaths as % of 1939 population'] = G
df['Average deaths as % of 1939 population'] = H
df['Military wounded'] = I
df
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_18204/2172105997.py in <module>
15 A.append("Blank")
16 else:
---> 17 A.append(country_link[0].contents[0].strip())
18 if cells[1].find(text=True) == None :
19 B.append("Blank")

TypeError: 'NoneType' object is not callable

Biffmotron
Jan 12, 2007

Looks like you're using BeautifulSoup to parse HTML. The basic error is that you're expecting something from the right_table Soup object which isn't there. The code is expecting a country_link[0].contents to be a bs Tag, or another list-like, and instead it's None. So that's the immediate error. I'd poke around at cells and see what it is, and why it's not what you expect it to be.

From another philosophical sense, there are two ways to build Pandas DataFrames, and I've found the column-oriented method you're using to be more brittle. For one, this only works on tables with 9 columns. And if the A-I lists are not all the same length, you'll also get an error.

The one thing about HTML tables is that they're highly structured. If I were doing this, I'd probably find a way to pull the header row out to use as column names, and then build my DataFrame from a row-oriented structure. The [*] operator might be a way to get your data into something more tractable than bs Tags. Like cells = [*row]

Mycroft Holmes
Mar 26, 2010

by Azathoth

Biffmotron posted:

Looks like you're using BeautifulSoup to parse HTML. The basic error is that you're expecting something from the right_table Soup object which isn't there. The code is expecting a country_link[0].contents to be a bs Tag, or another list-like, and instead it's None. So that's the immediate error. I'd poke around at cells and see what it is, and why it's not what you expect it to be.

From another philosophical sense, there are two ways to build Pandas DataFrames, and I've found the column-oriented method you're using to be more brittle. For one, this only works on tables with 9 columns. And if the A-I lists are not all the same length, you'll also get an error.

The one thing about HTML tables is that they're highly structured. If I were doing this, I'd probably find a way to pull the header row out to use as column names, and then build my DataFrame from a row-oriented structure. The [*] operator might be a way to get your data into something more tractable than bs Tags. Like cells = [*row]

No, I'm expecting It to be blank. There's nothing in the cell is the problem. I need my if, else statement to either replace a blank with text, of go ahead and just print the number. How should I rewrite the code to do that?

worms butthole guy
Jan 29, 2021

by Fluffdaddy
Hey all I have a quick question.

I'm using Hubspot API to call some data. I don't ever really use Python but figured my end goal would be best suited by Python (inserting into a database and sending a email).

Anyways I have some data that looks like this if I run print(data):

code:
{'completed_at': datetime.datetime(2022, 4, 18, 20, 18, 7, 103000, tzinfo=tzutc()),
 'links': None,
 'requested_at': None,
 'results': [{'archived': False,
              'archived_at': None,
              'created_at': datetime.datetime(2022, 1, 7, 2, 0, 38, 500000, tzinfo=tzutc()),
              'id': '9399',
              'properties': {'beach': '1',
                             'check_in_efficiency': '10',
                             'check_out_efficiency': '10',
                             'cleanliness_of_unit': '10',
                             'front_desk_staff': '10'
              'properties_with_history': {},
              'updated_at': datetime.datetime(2022, 1, 13, 6, 28, 48, 421000, tzinfo=tzutc())}],
 'started_at': datetime.datetime(2022, 4, 18, 20, 18, 7, 90000, tzinfo=tzutc()),
 'status': 'COMPLETE'}
I can't figure out the right way to print out "check_in_efficiency" under properties. I tried

print(data.results.properties.check_in_efficiency) but that doesn't want to work and gives me the below error:

code:
AttributeError: 'list' object has no attribute 'properties'
hoping someone can help me out with this. I imagine its something stupid.

Thanks!

EDIT: I figured it out, I forgot this was a object with several uh objects in it, so I had to be obvious about it and do a foreach over it.

worms butthole guy fucked around with this message at 21:29 on Apr 18, 2022

Biffmotron
Jan 12, 2007

Mycroft Holmes posted:

No, I'm expecting It to be blank. There's nothing in the cell is the problem. I need my if, else statement to either replace a blank with text, of go ahead and just print the number. How should I rewrite the code to do that?

Looks like the source data is https://en.wikipedia.org/wiki/World_War_II_casualties.

I ran your code, and it fails on the first line, which is Albania, but if you ignore that first Country Name column, everything else works. Looking at the types involved in the column, most of them are bs4.element.NavigableString, but some are bs4.element.Tag, and those are the ones that are failing. .strip() isn't a valid method for bs4.Tags, which is showing up as "TypeError: 'NoneType' object is not callable".

There might be a more elegant method, but the most straight forward is to write a function that takes in an unknown bs4 object, and if it's a NavigableString returns the strip() version, and if it's a Tag does something else to get the right output. I had some luck with .attrs['title'] for Tags, but I can't guarantee it'll always work. Web scraping is often about messy data.

QuarkJets
Sep 8, 2008

Mycroft Holmes posted:

I'm working on an assignment for class where I have to scrape a table from wikipedia, put it in a dataframe, display it, then write it to a csv. I am having trouble displaying it. Some of the rows have a null value, and my cod is repeatedly giving a NoneType error. I have attempted to put in an if, else statement that will replace the null value with "Blank." This is replacing all my values with blank. Here is my code and the error:

code:
A = []
B = []
C = []
D = []
E = []
F = []
G = []
H = []
I = []
for row in right_table.find_all('tr'):
    cells = row.find_all('td')
    if len(cells) == 9:
        country_link = cells[0].find_all('a')
        if cells[0].find(text=True) == None :
            A.append("Blank")
        else:
            A.append(country_link[0].contents[0].strip())
        if cells[1].find(text=True) == None :
            B.append("Blank")
        else:
            B.append(cells[1].find(text=True).strip())
        if cells[2].find(text=True) == None :
            C.append("Blank")
        else:
            C.append(cells[2].find(text=True).strip())
        if cells[3].find(text=True) == None :
            D.append("Blank")
        else:
            D.append(cells[3].find(text=True).strip())
        if cells[4].find(text=True) == None :
            E.append("Blank")
        else:
            E.append(cells[4].find(text=True).strip())
        if cells[5].find(text=True) == None :
            F.append("Blank")
        else:
            F.append(cells[5].find(text=True).strip())
        if cells[6].find(text=True) == None :
            G.append("Blank")
        else:
            G.append(cells[6].find(text=True).strip())
        if cells[7].find(text=True) == None :
            H.append("Blank")
        else:
            H.append(cells[7].find(text=True).strip())
        if cells[8].find(text=True) == None :
            I.append("Blank")
        else:
            I.append(cells[8].find(text=True).strip())
import pandas as pd
df = pd.DataFrame()
df['Country'] = A
df['Total Population 1/1/1939'] = B
df['Military deaths from all causes'] = C
df['Civilian deaths from military actions and crimes against humanity'] = D
df['Civilian deaths from war-related famine and disease'] = E
df['Total Deaths'] = F
df['Deaths as % of 1939 population'] = G
df['Average deaths as % of 1939 population'] = H
df['Military wounded'] = I
df
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_18204/2172105997.py in <module>
15 A.append("Blank")
16 else:
---> 17 A.append(country_link[0].contents[0].strip())
18 if cells[1].find(text=True) == None :
19 B.append("Blank")

TypeError: 'NoneType' object is not callable

I have some suggestions

You’ve got the same bit of code copy-pasted 9 times with a couple of characters changed. This will be a lot cleaner as a function

code:

def return_cell_string(cell):
    cell_value = cell.find(text=True)
    if cell_value is None:
        cell_value = "Blank" 
    return cell_value.strip()

# loop code is down here 
...

B.append(return_cell_string(cells[1])) 
C.append(return_cell_string(cells[2]))
D.append(return_cell_string(cells[3]))
etc...

You can see how much easier this is to read, and it's harder for mistakes to creep in. After that you should be able to figure out how to use clever iteration to combine all of those append lines into a very short for loop

As for your error, it's saying that a 'NoneType' object is not callable. This means that an object is actually None but you're trying to call it, as though it isn't None. This is the line it is pointing at:

A.append(country_link[0].contents[0].strip())

A is a list, clearly not None. What about country_link[0] or country_link[0].contents[0]? This is the point where you need to investigate the data further to figure out where you are getting a None, and why. Here is a hint: your if statement is checking the value of cells[0].find(text=True) but then you are appending something else entirely: (country_link[0].contents[0].strip()). You need to either change the if statement or the append.

SirPablo
May 1, 2004

Pillbug
Use the pandas read_html function, or is that cheating?

CarForumPoster
Jun 26, 2013

⚡POWER⚡

SirPablo posted:

Use the pandas read_html function, or is that cheating?

Ya do this. It returns a list tho so be aware of that.

When I am scraping html tables and need fairly fast code though I’ll use lxml’s etree and for loop over it getting whatever I want. Eg

For tr in trs: where trs is a list of tr html elements i got using the their xpath. then get the nested tds from each tr and for loop over them again. This is very slow in selenium but fast in lxml.

Also this page is super helpful for understanding xpaths https://www.guru99.com/xpath-selenium.html

Fender
Oct 9, 2000
Mechanical Bunny Rabbits!
Dinosaur Gum

CarForumPoster posted:

Ya do this. It returns a list tho so be aware of that.

When I am scraping html tables and need fairly fast code though I’ll use lxml’s etree and for loop over it getting whatever I want. Eg

For tr in trs: where trs is a list of tr html elements i got using the their xpath. then get the nested tds from each tr and for loop over them again. This is very slow in selenium but fast in lxml.

Also this page is super helpful for understanding xpaths https://www.guru99.com/xpath-selenium.html

Getting good at xpath is all kinds of useful in my current job. One thing I don't see mentioned a lot is that you can type xpath directly into the Chrome console and figure out exactly what you're selecting. No add-ons required.

Type this in the Chrome Console:
code:
$x("//div[@id='postpreview']")

CarForumPoster
Jun 26, 2013

⚡POWER⚡

Fender posted:

Getting good at xpath is all kinds of useful in my current job. One thing I don't see mentioned a lot is that you can type xpath directly into the Chrome console and figure out exactly what you're selecting. No add-ons required.

Type this in the Chrome Console:
code:
$x("//div[@id='postpreview']")

You don’t need to do it in console you can do it by Ctrl+f in the source panel. Just put in the xpath like you would any text to find.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Having another :smuggo: moment at my old self when I realized I don't need to have separate base_user and base_admin templates. I can use the jinja2 body_attributes tag with if/else conditionals in order to give a class to the body and then use body.admin / nav.admin and body.user / nav.user in the CSS to do the light blue background/dark blue navbar for users and light purple/dark purple for admin mode.

punk rebel ecks
Dec 11, 2010

A shitty post? This calls for a dance of deduction.
So I'm back from my traveling.

I managed to do what you all said and upload the code correctly on Github and make a pip freeze file for the requirements. That said the pip freeze resulted in pretty much every package I have installed in VSC listed in the file rather than what's simply required for the program. Not sure if I should delete the unnecessary stuff.

Anywhere he is the link to my project:

https://github.com/gunraidan/pokedex


Do you think I am ready to apply for any type of software development jobs yet?

CarForumPoster
Jun 26, 2013

⚡POWER⚡

punk rebel ecks posted:

So I'm back from my traveling.

I managed to do what you all said and upload the code correctly on Github and make a pip freeze file for the requirements. That said the pip freeze resulted in pretty much every package I have installed in VSC listed in the file rather than what's simply required for the program. Not sure if I should delete the unnecessary stuff.

Anywhere he is the link to my project:

https://github.com/gunraidan/pokedex


Do you think I am ready to apply for any type of software development jobs yet?

You can always apply. Theres a resume thread in BFC if you want resume advice. I hire entry level python engineers and have reviewed hundreds of resumes for them, hiring a hand full. Heres some advice:

If the goal of this github repo is to be part of a resume package that results in a python dev internship, it'd be about middle of the pack. So you're in the 50th% of python interns, but I only interview the top 20-25%.
Good: The actual code is readably-ish formatted with descriptive names.
Bad: It shows you don't work on teams or write code others will touch ever, which is a major negative.
- A team would need a readme to understand what the code does, how to install it, known issues, etc. Suggest including nice pics of it working properly or an animated GIF in the readme.md. Hiring people love gifs and this has a GUI.
- A team needs a requirements.txt which doesnt reference a file on "nomar's" desktop. This code references 6 non std lib packages, theres no way you need 125 dependencies for that. I'd assume you don't know how to use environments properly.
- A team needs comments and docstrings in the code itself that explains what the intended functions are or the expected interfaces and types are.
- They may also need tests, license info, and other stuff that I wouldnt expect from an intern.
Also, gunraidan is not an acceptable thing to name your professional portfolio.
EDIT: Also, PyQT is not a useful thing for 99+% of jobs. You'd be way better off making a web based pokedex with dash, flask, etc, then deploying it and providing a link in the readme.

You've improved from *instantly click reject* to "okay I'll take a look...yea this person shouldn't touch any useful code for a while I can safely pass on them."

Heres are two other pokedex projects that do this:
https://github.com/Tenchi2xh/pokedex-cli
https://github.com/arnavb/pypokedex
Don't these look way more professional?

CarForumPoster fucked around with this message at 05:30 on Apr 23, 2022

lazerwolf
Dec 22, 2009

Orange and Black

punk rebel ecks posted:

So I'm back from my traveling.

I managed to do what you all said and upload the code correctly on Github and make a pip freeze file for the requirements. That said the pip freeze resulted in pretty much every package I have installed in VSC listed in the file rather than what's simply required for the program. Not sure if I should delete the unnecessary stuff.

Anywhere he is the link to my project:

https://github.com/gunraidan/pokedex


Do you think I am ready to apply for any type of software development jobs yet?

Yes please only include the minimum requirements in your reqs file.

Also try to split your massive 1000 line file into smaller modules. Much easier to read and understand.

QuarkJets
Sep 8, 2008

punk rebel ecks posted:

So I'm back from my traveling.

I managed to do what you all said and upload the code correctly on Github and make a pip freeze file for the requirements. That said the pip freeze resulted in pretty much every package I have installed in VSC listed in the file rather than what's simply required for the program. Not sure if I should delete the unnecessary stuff.

Anywhere he is the link to my project:

https://github.com/gunraidan/pokedex


Do you think I am ready to apply for any type of software development jobs yet?

I'd strongly recommend working toward getting your project packaged and published to pypi. This is a pretty basic skill and may help illustrate some of the issues with your project.

I'm spotting some style inconsistencies in your code, a common sign of a novice. Install flake8 and run it on your code. This tool is designed to point out style and formatting differences between your code and what's commonly used in industry. Fix all of the things that it complains about. This is actually a pretty valuable exercise, use of good and consistent code style is a very important skill to learn but it takes a surprising amount of practice. Tools like flake8 will help you identify where you've failed to adhere to pep8

I'm going to ask some questions, think of this like a quiz. I'll bring up terms that you may not have seen before, and I'll be mentioning Python features that you should try to adopt in your code. These are gimme questions, I'd expect any prospective candidate to be able to respond confidently to all of them.

Python code:
#Essentials
import os
import sys

#Modules
from random import choice
import re
import requests
from string import Template
import threading
import time
Why did you label some of these imports as Essentials and others as Modules? Be specific.

Python code:
#Global Variables
path= os.path.dirname(__file__)
start = str(path)
Why did you call str() on path here? Do you understand what the str() function does? Do you understand what os.path.dirname() does?

Python code:
#Text Updater
class Update():
    
    changed_text = ""
    
    def __init__(self, text = None):
        self.text = text
        
    def get_text(self):
        return self.text
    
    def set_text(self, new_text):
       self.text = new_text
       
    def text_update(entered):
        pull = Update()
        pull.set_text(entered)
        result = pull.text
        template = Template("$text")
        text_string = template.safe_substitute(text = result)
        Update.changed_text = text_string
Since you have setters and getters here, have you considered using a property? If you have, explain your reasoning. If you have not, consider that now, and then explain your reasoning one way or the other.

Demonstrate how you would use the property decorator here.

Python code:
        self.height_cm = (int(Data.pokemon_dict["height"]) * 10)
        self.height_ft = self.height_cm * 0.0328084
        height_raw = str(self.height_ft).split(".")
        feet = height_raw[0]
        inches = (float("." + height_raw[1]) * 1.2)
        inches = float(round(inches,1)) * 10
        inches = str(int(inches))
        if inches == "12":
            feet = int(feet) + 1
            self.height_ft = ("~" + str(feet) + " ft")
        else:
            self.height_ft = feet + "'" + inches + '"'
        
        #Weight
        self.weight_kg = int(Data.pokemon_dict["weight"]) /10
        self.weight_lbs = round((self.weight_kg * 2.20462262),1)
        self.weight_lbs = (str(self.weight_lbs) + " lbs")
Why did you choose to convert inches to a string before comparing it to "12"?

(non-quiz comment: All of this string concatenation stuff should be replaced with f-strings)

Python code:
    def ability_list(self):
        abilities = []
        ability_count = len(Data.pokemon_dict["abilities"])
        for i in range (ability_count):
            names = Data.pokemon_dict["abilities"][i]['ability']['name']
            abilities.append(names.title())
            self.abilities = abilities
        return self.abilities
Can you eliminate this for loop and replace it with a list comprehension? Demonstrate that.

Python code:
        effect_urls = []
        effects_count = len(self.abilities)
        for i in range (effects_count):
            urls = Data.pokemon_dict["abilities"][i]['ability']['url']
            effect_urls.append(urls)
        self.effect_urls = effect_urls
Demonstrate how you would replace this for loop with a list comprehension.

Python code:
        attack_summary = []
        for i in range (len(self.attacks_levelup_name)):
            attack_summary.append(f"{self.attacks_levelup_name[i]}: {self.attacks_levelup_overview_list[i]} Learned at Level {self.level_learned[i]}.")
Demonstrate how you would replace this for loop with a list comprehension.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

punk rebel ecks posted:

So I'm back from my traveling.

I managed to do what you all said and upload the code correctly on Github and make a pip freeze file for the requirements. That said the pip freeze resulted in pretty much every package I have installed in VSC listed in the file rather than what's simply required for the program. Not sure if I should delete the unnecessary stuff.

Anywhere he is the link to my project:

https://github.com/gunraidan/pokedex


Do you think I am ready to apply for any type of software development jobs yet?

To add onto what others have said:

- Absolutely use a linter of some kind, like Flake8. This will significantly increase your code's readability and uniformity and help reinforce good coding habits. This also isn't a 'newbie crutch' thing either, all the professionals I know use Flake8 as well as auto-formatters like Black or autopep8 to ensure that you're sticking to consistent results.

- Breaking apart things into modules is not only good for readability, but also good for writing code. There's a lot of various styles or opinions on this, but In general, split your code out into modules based on what they do - you'll often see a types.py file in some smaller projects where you group general data types, or bigger classes can have their own files. You can overdo this, but I mostly go by 'feel'; when a file has gotten so big that it starts to feel clunky, it's a good sign that I need to look at refactoring some stuff out into it's own class.

- Also, I know that you've gotten some pretty significant critique here, but I do want to say that having anything on GitHub is pretty good, and being able to point to a finished project and say 'hey I made a thing, and it works!' is something to feel pretty good about. That said, having a project that addresses the critiques that everyone's brought up is an even better starting point.

punk rebel ecks
Dec 11, 2010

A shitty post? This calls for a dance of deduction.

QuarkJets posted:

I'd strongly recommend working toward getting your project packaged and published to pypi. This is a pretty basic skill and may help illustrate some of the issues with your project.

I'm spotting some style inconsistencies in your code, a common sign of a novice. Install flake8 and run it on your code. This tool is designed to point out style and formatting differences between your code and what's commonly used in industry. Fix all of the things that it complains about. This is actually a pretty valuable exercise, use of good and consistent code style is a very important skill to learn but it takes a surprising amount of practice. Tools like flake8 will help you identify where you've failed to adhere to pep8

I'm going to ask some questions, think of this like a quiz. I'll bring up terms that you may not have seen before, and I'll be mentioning Python features that you should try to adopt in your code. These are gimme questions, I'd expect any prospective candidate to be able to respond confidently to all of them.


QuarkJets posted:

Python code:
#Essentials
import os
import sys

#Modules
from random import choice
import re
import requests
from string import Template
import threading
import time
Why did you label some of these imports as Essentials and others as Modules? Be specific.

I should have really organized this better and used proper terms.

I used words that made sense "to me".

"#Essentials" referred to things that I will always use to import in my code.

In this case I always use "import os" and "import sys" as since virtually all my work involves GUI, these things will always need to be imported.

"#Modules" in my head are things that I import for the specific program. For example I don't typically import "threading" or "choice" into my programs.


QuarkJets posted:

Python code:
#Global Variables
path= os.path.dirname(__file__)
start = str(path)
Why did you call str() on path here? Do you understand what the str() function does?


I converted the path to a string because in PyQt you have to use strings to assign assets to button backgrounds and the like:

Python code:
 "{"
                               "background:transparent;"f"background-image : url({start}{directory}abilitiesh.png)"
                               "}"
                               "QPushButton:pressed"
Converting it to a string makes it fit snuggly in the text for "background-image".


QuarkJets posted:

Do you understand what os.path.dirname() does?

In simple terms, it finds the exact path of the python script in your computer.

So say if you download my GitHub code and you extract the file to your downloads folder. The python file will have the path: "C:\Users\QuarkJets\Downloads\PythonFileFolder".

If you take that folder and move it to say your desktop, then when the program runs os,path.dirname() will pull the path: "C:\Users\QuarkJets\Desktop\PythonFileFolder", instead.

The purpose of this is so when I tell python to retrieve an asset from the assets folder, it starts from the path os.parth.dirname() gets and scans the folder the python script it is in to see if it finds the directory path in the folder (in this case "/assets") and continues on until it finds the specific file to import.



QuarkJets posted:

Python code:
#Text Updater
class Update():
    
    changed_text = ""
    
    def __init__(self, text = None):
        self.text = text
        
    def get_text(self):
        return self.text
    
    def set_text(self, new_text):
       self.text = new_text
       
    def text_update(entered):
        pull = Update()
        pull.set_text(entered)
        result = pull.text
        template = Template("$text")
        text_string = template.safe_substitute(text = result)
        Update.changed_text = text_string
Since you have setters and getters here, have you considered using a property? If you have, explain your reasoning. If you have not, consider that now, and then explain your reasoning one way or the other.


I didn't bother because honestly this was a portable code snippet. It works fine without adding any decorators so I didn't see a reason to change anything.

QuarkJets posted:

Demonstrate how you would use the property decorator here..

Up until now I've treated this like a pop quiz and refused to look at my code or notes of any kind.

I've used decorators before (mostly the class method) but not too commonly.

Skimming my notes it seems that using the @property decorator be the most appropriate as it would involve setting the new text value and deleting the old one.

It isn't something that would be out of my ability at all if you gave me like fifteen minutes to play around in VSC, but being that I'm treating this like a real interview that's a pop quiz I'll just say I can't do it immediately off hand.



QuarkJets posted:

Python code:
        self.height_cm = (int(Data.pokemon_dict["height"]) * 10)
        self.height_ft = self.height_cm * 0.0328084
        height_raw = str(self.height_ft).split(".")
        feet = height_raw[0]
        inches = (float("." + height_raw[1]) * 1.2)
        inches = float(round(inches,1)) * 10
        inches = str(int(inches))
        if inches == "12":
            feet = int(feet) + 1
            self.height_ft = ("~" + str(feet) + " ft")
        else:
            self.height_ft = feet + "'" + inches + '"'
        
        #Weight
        self.weight_kg = int(Data.pokemon_dict["weight"]) /10
        self.weight_lbs = round((self.weight_kg * 2.20462262),1)
        self.weight_lbs = (str(self.weight_lbs) + " lbs")
Why did you choose to convert inches to a string before comparing it to "12"?

Even when writing this I knew somebody would bring this up.

To make things short, in my original code it was simply going to show height either by the original centimeters or converted inches. So I converted the float inches to a string because that was originally the final step. I decided it would please the eye more to further convert it to feet and inches. I ran the code and got an error, and saw that inches was a string so it couldn't be compared to the integer 12. So, instead of taking the string conversion out of the inches variable I just compared it to a string of 12.

It's lazy and uncharacteristic of me. And I will avoid doing similar things in the future.


QuarkJets posted:

Python code:
    def ability_list(self):
        abilities = []
        ability_count = len(Data.pokemon_dict["abilities"])
        for i in range (ability_count):
            names = Data.pokemon_dict["abilities"][i]['ability']['name']
            abilities.append(names.title())
            self.abilities = abilities
        return self.abilities
Can you eliminate this for loop and replace it with a list comprehension? Demonstrate that.

The term "list comprehension" seems extremely familiar. I've been taking a break from coding for half a month due to travel so I'll just glance at my notes as a refresher.

*Looks up notes for five seconds*

Oh that thing.

I don't use it that much as I feel it decreases readability of my code
+ , but if I had to write something off the top of my head:

Python code:
ability_count = len(Data.pokemon_dict["abilities"])

abilities_list = [x for x in ability_count]
This should make the abilities list import everything from the ability count list.

Unfortunately, this isn't what you want.

names = Data.pokemon_dict["abilities"][i]['ability']['name']

Pulls only the names from the list in the cache. So "ablitilies_list" should only contain the names. And for that I'm not entirely sure off the cuff. Again I could probably learn how to do this in less than a five minute internet search, but this is a "pop quiz" of sorts so I will honor that.

punk rebel ecks
Dec 11, 2010

A shitty post? This calls for a dance of deduction.
I'll be sure to use this Flake8 that people are talking about.

CarForumPoster posted:

If the goal of this github repo is to be part of a resume package that results in a python dev internship, it'd be about middle of the pack. So you're in the 50th% of python interns, but I only interview the top 20-25%.

I consider being "middle of the pack" a resounding victory being that I started at the bottom 100%.


CarForumPoster posted:

Good: The actual code is readably-ish formatted with descriptive names.

Yay!

CarForumPoster posted:

Bad: It shows you don't work on teams or write code others will touch ever, which is a major negative.

What is the best way for me to start working on a team? I assume it's working on an opensource project, but what is the best way to start?

CarForumPoster posted:

- A team would need a readme to understand what the code does, how to install it, known issues, etc. Suggest including nice pics of it working properly or an animated GIF in the readme.md. Hiring people love gifs and this has a GUI.

Oh cool. I actually love doing this kind of stuff! I even do something similar already at my job at times.

CarForumPoster posted:

- A team needs a requirements.txt which doesnt reference a file on "nomar's" desktop. This code references 6 non std lib packages, theres no way you need 125 dependencies for that. I'd assume you don't know how to use environments properly.
- A team needs comments and docstrings in the code itself that explains what the intended functions are or the expected interfaces and types are.
- They may also need tests, license info, and other stuff that I wouldnt expect from an intern.

I'll fix this.

CarForumPoster posted:

Also, gunraidan is not an acceptable thing to name your professional portfolio.

I was originally going to change it to something different but people on the Python discord assured me the name was fine.

I should have listened to my gut instinct and changed it.

CarForumPoster posted:

EDIT: Also, PyQT is not a useful thing for 99+% of jobs. You'd be way better off making a web based pokedex with dash, flask, etc, then deploying it and providing a link in the readme.

I've dabbled a bit with flask. I will exclusively use it for my next few projects then.

CarForumPoster posted:

You've improved from *instantly click reject* to "okay I'll take a look...yea this person shouldn't touch any useful code for a while I can safely pass on them."

Yay! I doubled my improvement from 100% to 50%. Now I just need to do it again then I can have an actual chance of landing a job somewhere!

CarForumPoster posted:

Heres are two other pokedex projects that do this:
https://github.com/Tenchi2xh/pokedex-cli
https://github.com/arnavb/pypokedex
Don't these look way more professional?

I will use these as references. Thank you for your comments.

lazerwolf posted:

Yes please only include the minimum requirements in your reqs file.

Also try to split your massive 1000 line file into smaller modules. Much easier to read and understand.

Will do.

Adbot
ADBOT LOVES YOU

QuarkJets
Sep 8, 2008

punk rebel ecks posted:

I should have really organized this better and used proper terms.

I used words that made sense "to me".

"#Essentials" referred to things that I will always use to import in my code.

In this case I always use "import os" and "import sys" as since virtually all my work involves GUI, these things will always need to be imported.

"#Modules" in my head are things that I import for the specific program. For example I don't typically import "threading" or "choice" into my programs.

But is sys really an "essential" import in every piece of code that you'll ever write? You should just drop those comments, they're not helpful. They also lead me to think that you don't understand what a module is.

quote:

I converted the path to a string because in PyQt you have to use strings to assign assets to button backgrounds and the like:

Python code:
 "{"
                               "background:transparent;"f"background-image : url({start}{directory}abilitiesh.png)"
                               "}"
                               "QPushButton:pressed"
Converting it to a string makes it fit snuggly in the text for "background-image".

os.path.dirname already returns a string, so it's not necessary to use str() to convert its output. Using str() here signals that you don't really understand what dirname() does; you should seek to remedy that.

More importantly, this is a novice-level way to form paths. You should use path.join instead, like this: os.path.join(start, directory, "abilitiesh.png"). This forms a path that's OS-agnostic and not dependent on including slashes in the directory pieces. You could alternatively look at pathlib if you wanted to use something even newer than os.path

quote:

In simple terms, it finds the exact path of the python script in your computer.

So say if you download my GitHub code and you extract the file to your downloads folder. The python file will have the path: "C:\Users\QuarkJets\Downloads\PythonFileFolder".

If you take that folder and move it to say your desktop, then when the program runs os,path.dirname() will pull the path: "C:\Users\QuarkJets\Desktop\PythonFileFolder", instead.

The purpose of this is so when I tell python to retrieve an asset from the assets folder, it starts from the path os.parth.dirname() gets and scans the folder the python script it is in to see if it finds the directory path in the folder (in this case "/assets") and continues on until it finds the specific file to import.

Don't say things like "in simple terms."

Your answer should basically be: "Given a path as a string, it returns the directory portion of the path, as a string." Short and sweet. Be specific about what the input and output is, and their acceptable types.

If you're invoking a function, it would really serve you well to look at its documentation. Here's this one's: https://docs.python.org/3/library/os.path.html#os.path.dirname

quote:

I didn't bother because honestly this was a portable code snippet. It works fine without adding any decorators so I didn't see a reason to change anything.

This sounds like "I didn't think about my code's design, I just kept writing code until it did what I wanted." Think of something else to say. Read up on properties and try to understand why you would or would not want to use them here.

quote:

Up until now I've treated this like a pop quiz and refused to look at my code or notes of any kind.

I've used decorators before (mostly the class method) but not too commonly.

Skimming my notes it seems that using the @property decorator be the most appropriate as it would involve setting the new text value and deleting the old one.

It isn't something that would be out of my ability at all if you gave me like fifteen minutes to play around in VSC, but being that I'm treating this like a real interview that's a pop quiz I'll just say I can't do it immediately off hand.

You should try actually doing this. Read about properties and then try using them in your code. "I could probably figure out how to use properties given enough time playing around" shows that you've probably never used them before - you need to change that. Get at least somewhat comfortable with being able to use them even if you don't use them routinely.

quote:

Even when writing this I knew somebody would bring this up.

To make things short, in my original code it was simply going to show height either by the original centimeters or converted inches. So I converted the float inches to a string because that was originally the final step. I decided it would please the eye more to further convert it to feet and inches. I ran the code and got an error, and saw that inches was a string so it couldn't be compared to the integer 12. So, instead of taking the string conversion out of the inches variable I just compared it to a string of 12.

It's lazy and uncharacteristic of me. And I will avoid doing similar things in the future.

Go ahead and change this code.

You should assign 0.0328084 to a descriptive variable name, such as "feet_to_cm = 0.0328084". Then "self.height_ft = self.height_cm * feet_to_cm".

You should try to avoid mutating inches between various types.

You're using strings to do math. Don't do that - it's a very novice move. Here's some specific code I'm referring to:

Python code:
        self.height_ft = self.height_cm * 0.0328084
        height_raw = str(self.height_ft).split(".")
        feet = height_raw[0]
        inches = (float("." + height_raw[1]) * 1.2)
        inches = float(round(inches,1)) * 10
        inches = str(int(inches))
        if inches == "12":
height_ft i sa float, which you convert to a string so that you can split the integer and decimal portions, you're multiplying the decimal portion by 1.2 (a magic number? what is this), then rounding and multiplying by 10, then converting to int, then a string, then comparing the string to "12". Yikes yikes yikes, this is a mess.

Python code:
height_ft = self.height_cm * feet_to_cm
height_inches = int(height_ft * 12)
if height_inches == 12:
etc. I'm having trouble parsing exactly what you're trying to do here, so this specifically probably isn't it, but everything that you want to do can definitely be done without resorting to string conversion. And if you have a pair of numbers, you should never be comparing their string representations; compare the numbers instead.

quote:

The term "list comprehension" seems extremely familiar. I've been taking a break from coding for half a month due to travel so I'll just glance at my notes as a refresher.

*Looks up notes for five seconds*

Oh that thing.

I don't use it that much as I feel it decreases readability of my code
+ , but if I had to write something off the top of my head:

Python code:
ability_count = len(Data.pokemon_dict["abilities"])

abilities_list = [x for x in ability_count]
This should make the abilities list import everything from the ability count list.

Unfortunately, this isn't what you want.

Pulls only the names from the list in the cache. So "ablitilies_list" should only contain the names. And for that I'm not entirely sure off the cuff. Again I could probably learn how to do this in less than a five minute internet search, but this is a "pop quiz" of sorts so I will honor that.

You're leaning to heavily on list lengths and should try to get more used to using iterators. "I need to get the length of the list, then iterate that many times to form another list of the same length while grabbing elements from the original list" basically sums up what you're thinking. A simpler approach would be "I need to iterate over list elements, and create a new list from them" - the list length is extraneous. Here's an example of your code without a list comprehension, but that uses iterators:

Python code:
    def ability_list(self):
        abilities = []
        for ability in Data.pokemon_dict["abilities"]:
            abilities.append(ability['ability']['name'].title())
        return abilities
That's a lot cleaner. Basically "ability" takes the place of your index, and you're just iterating over those abilities instead of indexing into the list of abilities. Something like that.

And here it is as a list comprehension:
Python code:
    def ability_list(self):
        abilities = [ability['ability']['name'].title() for ability in Data.pokemon_dict["abilities"]]
        return abilities
Even more succinct, and a lot more performant to boot. This could be made a lot better by cleaning up your data structures (why does "abilities" map to a list that are also abilities?), but you get the idea.

This is another instance where you need to be able to demonstrate knowledge. List comprehensions are a basic skill. "I could probably figure out how to write a list comprehension given enough time" is not the answer that an interviewer wants to hear, they want to hear "okay" followed by you briefly writing a list comprehension.

Another example:
Python code:
        effect_urls = []
        effects_count = len(self.abilities)
        for i in range (effects_count):
            urls = Data.pokemon_dict["abilities"][i]['ability']['url']
            effect_urls.append(urls)
        self.effect_urls = effect_urls
This becomes:
Python code:
        effect_urls = [ability['ability']['url'] for ability in Data.pokemon_dict["abilities"]]
You could state that you don't like using list comprehensions, because you dislike how they look in your code, but you still need to demonstrate that you know how to use them, and you definitely need to use iteration instead of range indexing.

You'll also need to accept that "I don't like using tools X, Y, and Z" is probably enough to disqualify you from a position on a team that routinely uses those tools. It's like applying for a carpentry job and saying "I don't like to use hammers or screwdrivers but could probably figure them out given enough time" - you could still be great at carpentry but that interviewer will probably pass on hiring you.

QuarkJets fucked around with this message at 22:39 on Apr 23, 2022

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