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
QuarkJets
Sep 8, 2008

D34THROW posted:

In my latest bit of surprising-myself code, I realized that a function I was writing with @singledispatch and would have violated DRY with could be boiled down quite easily - it takes an id, start date, and end date, builds a query, and executes it against the DB. returning the Decimal result.

Originally, it would have been get_sales_by_rep, get_sale_by_branch, etc. but I realized that I can map the table object names to a dict and use that to assign the table to get the FK `id` from. If that makes sense.

Python code:
def get_sales(
    name: Union[str, db.Model],
    id: int,
    date_start: datetime = None,
    date_end: datetime = None,
) -> Decimal:
    """ <docstring removed for brevity> """
    TABLE_IDS = {
        "Salesperson": Job.sales_rep_id,
        "Branch": Job.branch_id,
        "Municipality": Job.municipality_id,
        "User": Job.create_user_id,
        "Product": Job.product_id,
    }

    if not type(name) == str:
        name == name.__name__

    criterion = TABLE_IDS.get(name, None)

    if not criterion:
        # TODO Write some error logic here.
        pass

    if date_start and date_end:
        query = (
            db.select(func.sum([Job.contract_amount]))
            .filter(criterion == id)
            .filter(Job.contract_date >= date_start)
            .filter(Job.contract_date <= date_end)
        )
    elif date_start and not date_end:
        query = (
            db.select(func.sum([Job.contract_amount]))
            .filter(criterion == id)
            .filter(Job.contract_date >= date_start)
        )
    elif not date_start and date_end:
        query = (
            db.select(func.sum([Job.contract_amount]))
            .filter(criterion == id)
            .filter(Job.contract_date <= date_end)
        )
    elif not date_start and not date_end:
        query = db.select(func.sum([Job.contract_amount])).filter(
            criterion == id
        )

    return db.session.execute(query).all()[0]
Obviously a switch case would be more elegant here but I am stuck without upgrading for now. Just one of those moments that made me :unsmith:


EDIT: I've also completely eschewed sesson["ADMIN_MODE"] as something I'm using. I'm using flask-admin for that now, and you can only get to that index page with the proper roles. :v:

I HAVE COMMENTS

Python code:
def get_sales(
    name: Union[str, db.Model],
    id: int,
    date_start: datetime = None,
    date_end: datetime = None,
) -> Decimal:
id is a builtin function, even if you don't use that function you should still rename this argument to something else such as table_id
Python code:
    TABLE_IDS = {
        "Salesperson": Job.sales_rep_id,
        "Branch": Job.branch_id,
        "Municipality": Job.municipality_id,
        "User": Job.create_user_id,
        "Product": Job.product_id,
    }
You could probably avoid creating this lookup table every time that this function is called and just define it inside of whatever kind of object Job is. If you define __getitem__(self, name) to return the appropriate attribute given a specific label, then you can make the mapping part of the class. Kind of like this:

Python code:
class JobClass:
    def __init__(self):
        self.sales_rep_id = 10
        # etc.
        self.TABLE_IDS = {"Salesperson": self.sales_rep_id, }# etc.

    def __getitem__(self, name):
        return self.TABLE_IDS.get(name)

job = JobClass()
rep_id = job["Salesperson"]
Python code:
    if not type(name) == str:
        name == name.__name__
This does nothing, you probably meant to use single equals

If you use isinstance instead of type(name) == str then you can also accept string-derived types

Python code:
    criterion = TABLE_IDS.get(name, None)
This is ok just be aware that None is already the default, you don't have to specify it

Python code:
    if date_start and date_end:
        query = (
            db.select(func.sum([Job.contract_amount]))
            .filter(criterion == id)
            .filter(Job.contract_date >= date_start)
            .filter(Job.contract_date <= date_end)
        )
    elif date_start and not date_end:
        query = (
            db.select(func.sum([Job.contract_amount]))
            .filter(criterion == id)
            .filter(Job.contract_date >= date_start)
        )
    elif not date_start and date_end:
        query = (
            db.select(func.sum([Job.contract_amount]))
            .filter(criterion == id)
            .filter(Job.contract_date <= date_end)
        )
    elif not date_start and not date_end:
        query = db.select(func.sum([Job.contract_amount])).filter(
            criterion == id
        )
You should always be explicit when checking whether a value is actually None rather than checking its truthiness, by literally using "x is None" or "x is not None". Checking truthiness can give you unexpected results; a valid input may happen to be falsy

You've got a lot of repeated code here, you could simplify it a lot. Try this:

Python code:
        query = db.select(func.sum([Job.contract_amount])).filter(criterion == id)
        if date_start is not None:
            query = query.filter(Job.contract_date >= date_start)
        if date_end is not None:
            query = query.filter(Job.contract_date <= date_end)
I don't see where db, func, or Job are defined; globally-scoped variables in a function call is a code smell. Constants are the exception.

Python code:
    return db.session.execute(query).all()[0]
I could be mistaken, but it's very likely that what you actually want here is db.session.execute(query).first(), or some other method that provides just 1 row. This line could be returning a lot of rows needlessly, and if the database is big enough then you'll experience performance issues from lines like this one

Adbot
ADBOT LOVES YOU

punk rebel ecks
Dec 11, 2010

A shitty post? This calls for a dance of deduction.
Will having programming certificates from LinkedIn in specific things in Python help me at all in terms of a job interview? Like with Flask and other stuff?

I understand my code in my portfolio is what matters, but I'm hoping they may catch some people's eyes.

My job mandates me taking any LinkedIn certificate/classes (luckily they are all paid for).

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

punk rebel ecks posted:

Will having programming certificates from LinkedIn in specific things in Python help me at all in terms of a job interview? Like with Flask and other stuff?

I understand my code in my portfolio is what matters, but I'm hoping they may catch some people's eyes.

My job mandates me taking any LinkedIn certificate/classes (luckily they are all paid for).

i doubt it. i always forget linkedin certificates are a thing when looking at a candidates profile and when i see them i just scroll past without a second thought.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:

QuarkJets posted:

I HAVE COMMENTS

id is a builtin function, even if you don't use that function you should still rename this argument to something else such as table_id

I didn't like this either, I was gonna change it today.

quote:

You could probably avoid creating this lookup table every time that this function is called and just define it inside of whatever kind of object Job is. If you define __getitem__(self, name) to return the appropriate attribute given a specific label, then you can make the mapping part of the class. Kind of like this:

I realized upon reading this that I'll be referencing that mapping outside of that function, but all the functions that use that mapping will be in that module, so it'll probably end up a module-level constant, otherwise it'd be on every instance of Job and there's...a lot of those since it's a record from the ORM.

quote:

This does nothing, you probably meant to use single equals

That was causing errors and I fixed it :v:

quote:

If you use isinstance instead of type(name) == str then you can also accept string-derived types

This in particular was giving me trouble. I mess around in the interactive Flask shell before committing anything to code and I just could not get it to work.

quote:

This is ok just be aware that None is already the default, you don't have to specify it

I changed that too already.

quote:

You should always be explicit when checking whether a value is actually None rather than checking its truthiness, by literally using "x is None" or "x is not None". Checking truthiness can give you unexpected results; a valid input may happen to be falsy
You've got a lot of repeated code here, you could simplify it a lot. Try this:

That's already been pared down and simplified to a pair of if statements that append a filter to the query if the date is provided, but I will take it from an if date_end to if date_end is not None for sure.

quote:

I don't see where db, func, or Job are defined; globally-scoped variables in a function call is a code smell. Constants are the exception.

They're in the imports at the top of the module :v: db comes from application, func from sqlalchemy.sql, and Job from application.models.data_models

quote:

I could be mistaken, but it's very likely that what you actually want here is db.session.execute(query).first(), or some other method that provides just 1 row. This line could be returning a lot of rows needlessly, and if the database is big enough then you'll experience performance issues from lines like this one

I've mucked around with several methods in the REPL for this and that specific method is getting what I want. I don't like it either but for now it is what it is. all() returns a list of tuples,
first() returns a single tuple; it probably has to do with using func.sum even though it's loving annoying.


I appreciate y'all. I don't have a team, ergo I don't have anyone else to tell me I"m being a dumbfuck with my code :frogbon:


EDIT: I'm getting even cuter now. Removed the ability to pass a str as an argument and got cute with the mapping, using the models as keys. That way, I can eschew the isinstance() check.

Python code:
# imports

TABLE_XREF = {
    Salesperson: Job.sales_rep_id,
    Branch: Job.branch_id,
    Municipality: Job.municipality_id,
    User: Job.create_user_id,
    Product: Job.product_id,
}

def get_sales(model: db.Model, search_id: int, date_start: datetime = None, date_end: datetime = None,) -> Decimal:
    try:
        table_id = TABLE_XREF[model]
    except KeyError as err:
        raise Exception(f"Table '{model.__name__}' does not exist.").with_traceback(err.__traceback__)

    query = db.select([func.sum(Job.contract_amount)]).filter(table_id == search_id)

    if date_start is not None:
        query = query.filter(Job.contract_date >= date_start)
    if date_end is not None:
        query = query.filter(Job.contract_date <= date_end)

    return db.session.execute(query).first()[0]

D34THROW fucked around with this message at 17:31 on Jun 4, 2022

QuarkJets
Sep 8, 2008

D34THROW posted:

I didn't like this either, I was gonna change it today.

I realized upon reading this that I'll be referencing that mapping outside of that function, but all the functions that use that mapping will be in that module, so it'll probably end up a module-level constant, otherwise it'd be on every instance of Job and there's...a lot of those since it's a record from the ORM.

That was causing errors and I fixed it :v:

This in particular was giving me trouble. I mess around in the interactive Flask shell before committing anything to code and I just could not get it to work.

I changed that too already.

That's already been pared down and simplified to a pair of if statements that append a filter to the query if the date is provided, but I will take it from an if date_end to if date_end is not None for sure.

They're in the imports at the top of the module :v: db comes from application, func from sqlalchemy.sql, and Job from application.models.data_models

I've mucked around with several methods in the REPL for this and that specific method is getting what I want. I don't like it either but for now it is what it is. all() returns a list of tuples,
first() returns a single tuple; it probably has to do with using func.sum even though it's loving annoying.


I appreciate y'all. I don't have a team, ergo I don't have anyone else to tell me I"m being a dumbfuck with my code :frogbon:


EDIT: I'm getting even cuter now. Removed the ability to pass a str as an argument and got cute with the mapping, using the models as keys. That way, I can eschew the isinstance() check.

Python code:
# imports

TABLE_XREF = {
    Salesperson: Job.sales_rep_id,
    Branch: Job.branch_id,
    Municipality: Job.municipality_id,
    User: Job.create_user_id,
    Product: Job.product_id,
}

def get_sales(model: db.Model, search_id: int, date_start: datetime = None, date_end: datetime = None,) -> Decimal:
    try:
        table_id = TABLE_XREF[model]
    except KeyError as err:
        raise Exception(f"Table '{model.__name__}' does not exist.").with_traceback(err.__traceback__)

    query = db.select([func.sum(Job.contract_amount)]).filter(table_id == search_id)

    if date_start is not None:
        query = query.filter(Job.contract_date >= date_start)
    if date_end is not None:
        query = query.filter(Job.contract_date <= date_end)

    return db.session.execute(query).first()[0]

In most sql frameworks first() and all()[0] are equivalent, try dropping the square brackets from first().

necrotic
Aug 2, 2005
I owe my brother big time for this!

QuarkJets posted:

In most sql frameworks first() and all()[0] are equivalent, try dropping the square brackets from first().

I would be careful calling these equivalent! The underlying SQL query would not be the same. The resulting object should be equivalent, but what actually happens under the hood is not.

The first method would be the unobjectionable better approach here.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:

QuarkJets posted:

In most sql frameworks first() and all()[0] are equivalent, try dropping the square brackets from first().

SQLAlchemy is being a gently caress. Probably because the raw SQL is along the lines of SELECT sum(...) as sum_1 and it's returning a row as opposed to a single value.

Python code:
>>> db.session.execute(db.select([func.sum(Job.contract_amount)])).first()
(Decimal('476990.00'),)
>>> type(db.session.execute(db.select([func.sum(Job.contract_amount)])).first())
<class 'sqlalchemy.engine.row.Row'>

QuarkJets
Sep 8, 2008

necrotic posted:

I would be careful calling these equivalent! The underlying SQL query would not be the same. The resulting object should be equivalent, but what actually happens under the hood is not.

The first method would be the unobjectionable better approach here.

Yeah, that's what I said...

(the returned row is the same, the difference in query (and performance) is why I suggested to use first() instead of all()[0] in the first place)

D34THROW posted:

SQLAlchemy is being a gently caress. Probably because the raw SQL is along the lines of SELECT sum(...) as sum_1 and it's returning a row as opposed to a single value.

Python code:
>>> db.session.execute(db.select([func.sum(Job.contract_amount)])).first()
(Decimal('476990.00'),)
>>> type(db.session.execute(db.select([func.sum(Job.contract_amount)])).first())
<class 'sqlalchemy.engine.row.Row'>

Ah, you're summing; that piece makes more sense now.

Still, if you ever have a line that reads as all()[0] then you should just replace that with first(), they're going to give you the same result but first() is a lot more performant.

necrotic
Aug 2, 2005
I owe my brother big time for this!

QuarkJets posted:

Yeah, that's what I said...

(the returned row is the same, the difference in query (and performance) is why I suggested to use first() instead of all()[0] in the first place)

I see that now in your preceding post. I just saw the one I quoted in isolation.

Dawncloack
Nov 26, 2007
ECKS DEE!
Nap Ghost
Has there been some discussion on messed up dependencies?

I bet there has been. Essentially, I have a 3.8 version with MySQLdb installed and working, but I tried to install Matplotlib and it is not recognized. Same for NumPy, with shoes in the distribution directories.

Can someone point me to some resource to properly organize python dependencies? Ideally I'd like to install 3.10, erase previous version and make the modules work. I am willing to read and learn on my own but nothing I've found online has quite helped me.

Thanks in advance.

CarForumPoster
Jun 26, 2013

⚡POWER⚡

Dawncloack posted:

Has there been some discussion on messed up dependencies?

I bet there has been. Essentially, I have a 3.8 version with MySQLdb installed and working, but I tried to install Matplotlib and it is not recognized. Same for NumPy, with shoes in the distribution directories.

Can someone point me to some resource to properly organize python dependencies? Ideally I'd like to install 3.10, erase previous version and make the modules work. I am willing to read and learn on my own but nothing I've found online has quite helped me.

Thanks in advance.

You should be using an environment manager. These some pros and cons to easy. In general if you're having to manage installing binaries anaconda is usually your easiest bet. This was you can have projects on python 3.6, some on 3.9, whatever. Likewise if you need to install some software, often someone has packaged the binary such you can conda install [thing]

QuarkJets
Sep 8, 2008

Dawncloack posted:

Has there been some discussion on messed up dependencies?

I bet there has been. Essentially, I have a 3.8 version with MySQLdb installed and working, but I tried to install Matplotlib and it is not recognized. Same for NumPy, with shoes in the distribution directories.

Can someone point me to some resource to properly organize python dependencies? Ideally I'd like to install 3.10, erase previous version and make the modules work. I am willing to read and learn on my own but nothing I've found online has quite helped me.

Thanks in advance.

MySQLdb is an old Python2 package, whatever you have installed must be one of the many forks of that with Python3 support added.
Is this your MySQLdb?
https://mysqlclient.readthedocs.io/index.html
If so, that's pointing to this pypi package:
https://pypi.org/project/mysqlclient/
Use pip to check what packages are installed, maybe try updating this one

Alternatively, the easiest/standard way to manage dependencies (including non-python dependencies like MySQL) is to use conda environments. Conda environments are created and managed with the command line tool "conda", or its faster replacement "mamba".
Install mambaforge.
Create a new conda environment for python and whatever other packages you want (mamba create -n new_environment_name python=3.9 matplotlib numpy)
Activate the environment (conda activate new_environment_name)
If you need to add other packages to it, once activated you can use "mamba install package_name" or "pip install package_name". The big repositories for conda packages are conda-forge and anaconda, you can easily search those places if you are looking for something

QuarkJets fucked around with this message at 18:30 on Jun 15, 2022

Dawncloack
Nov 26, 2007
ECKS DEE!
Nap Ghost
This forums rule,and you guys rule.

Thanks! :)

duck monster
Dec 15, 2004

Dawncloack posted:

Has there been some discussion on messed up dependencies?

I bet there has been. Essentially, I have a 3.8 version with MySQLdb installed and working, but I tried to install Matplotlib and it is not recognized. Same for NumPy, with shoes in the distribution directories.

Can someone point me to some resource to properly organize python dependencies? Ideally I'd like to install 3.10, erase previous version and make the modules work. I am willing to read and learn on my own but nothing I've found online has quite helped me.

Thanks in advance.

Use a virtualenv, theres a few different flavors but Pipenv is popular although it can be temperamental.

Also, I need to tell you about our lord and savior, Postgres. Postgres loves you. He just asks that you accept him in your heart.

Dawncloack
Nov 26, 2007
ECKS DEE!
Nap Ghost

duck monster posted:

Postgres. Postgres loves you.
I am saved! I am a born-again databaser now!

Jokes aside, yeah, the peepz I'm doing a thing with have already decided to ditch MySQL in favour of Postgres, come next iteration. I took some time to figure out users and permissions at the beginning but it is super neat.

Cyril Sneer
Aug 8, 2004

Life would be simple in the forest except for Cyril Sneer. And his life would be simple except for The Raccoons.
I'm working with very large lists-of-dictionaries where those dictionaries each include a large 2D numpy array of floats (amongst other keys). I have a few processing steps where I want to sort this list into two lists based on a certain condition on the numpy array - basically a keep list and a reject list - the logic here isn't challenging, but in terms of speed and/or memory use is it better to use a modify-in-place approach (iterating over the list backward and popping the unwanted elements) or creating a new list via comprehension?

I realize there may not be a universal answer here.

ExcessBLarg!
Sep 1, 2001
Roughly how big are your lists? How often do you anticipate having to actually modify them (e.g., "rarely" vs. "frequently")?

You might have to benchmark it.

Cyril Sneer
Aug 8, 2004

Life would be simple in the forest except for Cyril Sneer. And his life would be simple except for The Raccoons.

ExcessBLarg! posted:

Roughly how big are your lists? How often do you anticipate having to actually modify them (e.g., "rarely" vs. "frequently")?

You might have to benchmark it.

~200,000 entries each containing a 256x256 image.

There are 3 steps I perform. First loop is a zero-check (np.all applied to each image); second loop is more complicated as I'm checking certain pixels ranges for certain features; then finally I resize everything to a new size (~80 x 80).

What I've been finding is that my loops start fast but slow to a nearly-unusable crawl toward the end and I'm not really sure why.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

Cyril Sneer posted:

~200,000 entries each containing a 256x256 image.

There are 3 steps I perform. First loop is a zero-check (np.all applied to each image); second loop is more complicated as I'm checking certain pixels ranges for certain features; then finally I resize everything to a new size (~80 x 80).

What I've been finding is that my loops start fast but slow to a nearly-unusable crawl toward the end and I'm not really sure why.

Assuming that you're not discussing a major overhaul that would take a long time to code, I'd do...both and https://docs.python.org/3/library/timeit.html.

Basically I suspect that you're trading memory utilization for speed - creating two new lists is going to probably use more memory (although not a ton if my understanding of Python references is correct). On the other hand, reverse iterating and popping is probably not a lot of overhead since you're basically doing O(n).

If your loops are crawling at the end I'd measure memory usage or figure out if something isn't getting cleared, or if you're doing some sort of extra iteration or something, like checking your good/bad lists each time you add something to them.

QuarkJets
Sep 8, 2008

Wouldn't a pair of list comprehensions be O(2N) while popping is O(N)? You still have the extra interpreter overhead but for 200k entries you're surely better off popping. Or I guess the resize part is still O(N) but the conditionals are all being checked twice

I would also assume that you're slowing down because you're exceeding a memory limitation, that's simply a lot of data for 1 machine

QuarkJets fucked around with this message at 21:48 on Jun 17, 2022

CarForumPoster
Jun 26, 2013

⚡POWER⚡
Good advice above, adding: doing experimentation like this in jupyter notebooks is my fav. You can just throw a %time at the top of the cell and see how long it took versus the other way you were considering. Just write prototype code for each, then delete the slow one. Once the cells work in sequence, make it a .py file.

IMO this method of developing poo poo is way better at catching gotchas/slow code/errors than writing things in a .py file and testing them over and over as a monolith.


...though doesnt work well for things like django/flask/fastapi webapps.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

CarForumPoster posted:

Good advice above, adding: doing experimentation like this in jupyter notebooks is my fav. You can just throw a %time at the top of the cell and see how long it took versus the other way you were considering. Just write prototype code for each, then delete the slow one. Once the cells work in sequence, make it a .py file.

IMO this method of developing poo poo is way better at catching gotchas/slow code/errors than writing things in a .py file and testing them over and over as a monolith.


...though doesnt work well for things like django/flask/fastapi webapps.

Yeah, and remember that VSCode basically works with Jupyter notebooks out of the box if you don't want to go through the whole install process/etc. It's very convenient for any data munching work.

Zephirus
May 18, 2004

BRRRR......CHK
It's worth pointing out that memray exists and is good for figuring out if you have memory issues, and can trace native code.

https://github.com/bloomberg/memray

Seventh Arrow
Jan 26, 2005

I'm going through the Codecademy python course and there were some exercises combining functions and list stuff that kind of stymied me. I admittedly had to look up the solution, but I was at least able to solve some of it on my own.

Here's the gist of the exercise:

quote:

For the final code challenge, we are going to create a function that finds the middle item from a list of values. This will be different depending on whether there are an odd or even number of values. In the case of an odd number of elements, we want this function to return the exact middle value. If there is an even number of elements, it returns the average of the middle two elements. Here is what we need to do:

  1. Define the function to accept one parameter for our list of numbers
  2. Determine if the length of the list is even or odd
  3. If the length is even, then return the average of the middle two numbers
  4. If the length is odd, then return the middle number

Furthermore,

quote:

Create a function called middle_element that has one parameter named lst.

If there are an odd number of elements in lst, the function should return the middle element. If there are an even number of elements, the function should return the average of the middle two elements.

Here's the solution:

code:
def middle_element(lst):
  if len(lst) % 2 == 0:
    sum = lst[int(len(lst)/2)] + lst[int(len(lst)/2) - 1]
    return sum / 2
  else:
    return lst[int(len(lst)/2)]

print(middle_element([5, 2, -10, -4, 4, 5]))
My questions:

  • What happens if one doesn't use 'int'? I kind of perceive that it's casting the value to an integer, but isn't it an integer to begin with?
  • For the 'else' statement, it's dividing the length of an odd-numbered list by two...is it just rounding the result up to three?
  • I see that when a function interacts with a list, sometimes it needs the square brackets and sometimes it doesn't (like when doing a count). Why is this?

And as an added bonus: I sometimes get the basic idea of how to solve something, but the specific syntaxes or rules get jumbled in my head. For example, in another definition I had a function that didn't work because I was using sort() instead of sorted and the former doesn't return a value while the latter does. Or there was one exercise where I had to take an existing list and make a new list with it but with the middle item removed, and I couldn't figure it out. Is there a way to get that kind of thing organized in my head?

QuarkJets
Sep 8, 2008

Seventh Arrow posted:

My questions:

  • What happens if one doesn't use 'int'? I kind of perceive that it's casting the value to an integer, but isn't it an integer to begin with?
  • For the 'else' statement, it's dividing the length of an odd-numbered list by two...is it just rounding the result up to three?
  • I see that when a function interacts with a list, sometimes it needs the square brackets and sometimes it doesn't (like when doing a count). Why is this?

And as an added bonus: I sometimes get the basic idea of how to solve something, but the specific syntaxes or rules get jumbled in my head. For example, in another definition I had a function that didn't work because I was using sort() instead of sorted and the former doesn't return a value while the latter does. Or there was one exercise where I had to take an existing list and make a new list with it but with the middle item removed, and I couldn't figure it out. Is there a way to get that kind of thing organized in my head?

1. You can verify this for yourself, by opening a Python terminal and running "type(4/2)". In my terminal, this returns a float. To get an int you'd use // instead of /
2. It's grabbing the middle element. I'm not sure what you mean by "is it just rounding the result up to three". If there's an odd number of elements, then the index of the center element is floor(len(x) / 2). Since it's odd-length, the length divided by two leaves with with a remainder; using int() truncates that remainder. If the list length is 3, you want index 1.
3. Square brackets are only used to access entries in a list

If you're not 100% certain about how something works, then you should use the internet to look up that information, or in a python terminal you can run "help(sorted)" or "help(list.sort)". Or you can write out a little toy script that tests some specific functionality that you're not sure about, if it's not as simple as "hey uhh I forgot exactly what this function does". I've been writing Python code for 20 years and I still use help on basic functions that I haven't used in awhile.

12 rats tied together
Sep 7, 2006

The square brackets are what python calls a "subscription". It's from a math thing that I can't fully explain but is probably available on the internet. The main implication, IMHO, is that the item in the subscript is a member of (exists in) the container.

Under the hood, when you call "[1]" on some python thing, you're (usually) calling thing.__getitem__(1)
Python code:
>>> l = [1,2,3]
>>> l[1]
2
>>> l.__getitem__(1)
2

>>> dir(l)
[ [...],  '__class_getitem__' [...], '__getitem__', [...]]
Sometimes you're calling .__class_getitem__, the documentation goes into more detail about when and why. The square brackets are a syntax sugar for calling a method on an object, just like you might call e.g. l.count() to get the count of a number of times an item appears the list.

Seventh Arrow
Jan 26, 2005

Thanks for the help! That gives me a lot to mull over.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

QuarkJets posted:

If you're not 100% certain about how something works, then you should use the internet to look up that information, or in a python terminal you can run "help(sorted)" or "help(list.sort)". Or you can write out a little toy script that tests some specific functionality that you're not sure about, if it's not as simple as "hey uhh I forgot exactly what this function does". I've been writing Python code for 20 years and I still use help on basic functions that I haven't used in awhile.

I keep an ipython interactive interpreter window open most of the time I'm coding for just this purpose. "Wait, can I add two dictionaries together with +? Let me find out...yep!" etc.

Data Graham
Dec 28, 2009

📈📊🍪😋



Yeah definitely. And in case anybody doesn't know, one of the things that makes Django development such a joy is that you can ./manage.py shell and it will let you tinker arbitrarily with Python within the context of your webapp, do database operations, import and run methods, all kinds of stuff. And if you have ipython installed, it automatically invokes that instead of your regular REPL, so you get CLI history, command completion, all the cool features.

duck monster
Dec 15, 2004

Dawncloack posted:

I am saved! I am a born-again databaser now!

Then we should say the sinners prayer.

Oh Postgres,
I I've sinned and used MySQL.
I promise I will learn about Normalization.
I promise I will understand how triggers , functions and views work.
I promise I will bake the bosses noodle by creating a custom foreign data wrapper
that can drive the office coffee machine using database inserts.
I promise I will use JSON fields sparingly.
I promise I will learn to index wisely.
I promise I will learn and embrace the gospel of EXPLAIN and EXPLAIN ANALYSE.
I promise I will learn to partit...wait you mean ..... loving non-ordinal key?.... gently caress. HAIL SATAN.

Seventh Arrow
Jan 26, 2005

Another Codecademy woe. This time I don't feel so bad because I'm pretty sure they haven't covered this before. Anyways,

The lesson is teaching about iterating over strings, and here is the challenge:

quote:

Write a function called common_letters that takes two arguments, string_one and string_two and then returns a list with all of the letters they have in common.

The letters in the returned list should be unique. For example,

common_letters("banana", "cream")
should return ['a'].

I'm just going to go ahead and post the solution:

code:
def contains(big_string, little_string):
  return little_string in big_string

def common_letters(string_one, string_two):
  common = []
  for letter in string_one:
    if (letter in string_two) and not (letter in common):
      common.append(letter)
  return common
Unfortunately, I blew away my solution, but it was something more along the lines of:

code:
def contains(big_string, little_string):
  return little_string in big_string

def common_letters(string_one, string_two):
  common = []
  for l in string_one:
    if l in string_one[l] == string_two[l]:
      common.append(l)
  return common
Which just gave me type errors. Is that kind of iteration only for integers? I've seen before that you can slice strings with square brackets, so I'm not sure what the problem is (I'm not even sure what I wrote would work with integers, the continual use of the "l" seems off).

Also, what is going on in
code:
 if (letter in string_two) and not (letter in common):
? I get that there's an if statement and a boolean, and even the "in" boolean used for strings, but what do the brackets add? I did a search, but I wasn't sure what kind of terms to use for something so situational.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Alternate solution use sets and intersection of the two lists

ArcticZombie
Sep 15, 2010
When you index a string, you use an integer to pull out the nth character (starting from zero):

Python code:
>>> s = "foobar"
>>> n = 3
>>> s[n]
'b'
What do you think that the variable l is in each iteration of the loop for l in string_one? What would this output?

Python code:
s = "foobar"
for l in s:
    print(l)
Do you think it makes sense to try and pull out the lth character of a string?

The parentheses in that conditional are doing nothing, they are just an attempt to make it easier for you, a human, to separate out the conditions being tested from the syntax of an if statement.

12 rats tied together
Sep 7, 2006

Phone posting but you should try "for l in string_one, print l". The type error I imagine is because you're using a letter as a list index, but list indexes should only be integers.

e: poster above me said it much better :tipshat:

boofhead
Feb 18, 2021

Dunno how much this will help but if you throw the below example into python (an online compiler can be helpful for simple stuff) and run it, it might help you visualise what's happening when you try to loop through the index and values of an array

(edit: in the way that you've tried! Other people are very correct in saying that if you need to iterate through a list, just do it, you don't need the index for what you're doing. This post is because it looked like you might be struggling with arrays and indices in general, for eg if you've tried dabbling in JavaScript where it's a lot more common in beginner courses.. or at least it used to be)

Python code:
def split_string(text):
    text_length = len(text)
    print(f"Text: '{text}' Length: {text_length}")
    print(list(text))
    for index in range(text_length):
        character = text[index]
        print(f"Index: {index} Character: {character}")
        
split_string("butts")
split_string("Each element in an array has a positional index.")
split_string("Arrays can be different lengths.")
Sample results for butts:

Python code:
Text: 'butts' Length: 5
['b', 'u', 't', 't', 's']
Index: 0 Character: b
Index: 1 Character: u
Index: 2 Character: t
Index: 3 Character: t
Index: 4 Character: s
fe: If you want to think more about what

Python code:
for index in range(text_length)
is doing, consider:

Python code:
print(list(range(10)))

# => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(range() is a special function so you just need to convert the output into a list to display it properly)

boofhead fucked around with this message at 00:33 on Jun 25, 2022

QuarkJets
Sep 8, 2008

Seventh Arrow posted:

Which just gave me type errors. Is that kind of iteration only for integers? I've seen before that you can slice strings with square brackets, so I'm not sure what the problem is (I'm not even sure what I wrote would work with integers, the continual use of the "l" seems off).

That kind of iteration is valid for any kind of iterable. A string is an iterable of characters. When you iterate over a string, you're getting characters back. Accessing elements in an iterable with square brackets (slicing or not) requires an integer

If you wanted to iterate over indices, you'd have to do something else such as using range() to iterate over the string's length. Check out the outputs of these loops:
Python code:
for c in some_string:
    print(c)
Python code:
for i in range(len(some_string)):
    print(i, some_string[i])
Newer programmers tend to want to use string indices like this because it often makes intuitive sense, but you should resist that urge if you experience it. Iterating over the characters directly is a lot cleaner. The instructor's solution is good, here's a slightly less good solution that I think is what you were trying to do:

Python code:
def common_letters(string_one, string_two):
  common = []
  for character_one in string_one:
    for character_two in string_two:
      if character_one == character_two
        if character_one not in common:
          common.append(character_one)
  return common
That's not as good for a few reasons; the second loop over characters is a less efficient version of just using "in".

I'm going to present some things that I think are cool but probably go beyond your lesson plan, feel free to ignore them. Here's a set comprehension:

Python code:
def common_letter(string_one, string_two):
    return list({c for c in string_one if c in string_two})
Or you can do this with a set intersection:

Python code:
def common_letter(string_one, string_two):
    return list(set(string_one).intersection(string_two))

Seventh Arrow
Jan 26, 2005

Sad Panda posted:

Alternate solution use sets and intersection of the two lists

Usually their exercises don't go beyond what the course has taught up until that point, though. However, I will look up sets because it sounds intriguing! Thanks!



ArcticZombie posted:


What do you think that the variable l is in each iteration of the loop for l in string_one? What would this output?

The parentheses in that conditional are doing nothing, they are just an attempt to make it easier for you, a human, to separate out the conditions being tested from the syntax of an if statement.

I thought that "l" if you're doing "for l in string_one..." the l is treated as a kind of temporary variable and represents each individual character in string_one. Or am I way off?

Thank you for letting me know about the parentheses, this is kind of what I suspected.


12 rats tied together posted:

Phone posting but you should try "for l in string_one, print l". The type error I imagine is because you're using a letter as a list index, but list indexes should only be integers.


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

Seventh Arrow
Jan 26, 2005

QuarkJets, that makes a lot of sense, thank you!

12 rats tied together
Sep 7, 2006

Seventh Arrow posted:

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

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

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

Adbot
ADBOT LOVES YOU

QuarkJets
Sep 8, 2008

Yo Seventh Arrow check this out

Python code:

for i, c in enumerate(some_string): 
    print(i, some_string[i], c)

Not very helpful here but I thought you might be interested in seeing what enumerate does

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