Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python SQLAlchemy Basics Working with SQLAlchemy Creating Our Book Database

Andy McDonald
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Andy McDonald
Python Development Techdegree Graduate 13,801 Points

Using variable as attribute

I started writing the following code:

def edit():
    toedit = str(input('What book do you want to edit'))
    part = input('What do you want to change about it? \nType number to select follwing: \n1. Title \n2. Author\n3. Published date\n4. Price\n')
    if part == '1':
        change = input('Okay, what do you want to change the title to?')
        bookie = models.session.query(models.Book).filter_by(title=toedit)
        for bkie in bookie:
            bkie.title = change
        print(bkie)

And repeated this block for each attribute of the object. And then realized I could make it real dry by only writing the bottom part of the block once and throwing some variables in there like so:

    elif part == '3':
        part = 'pubdate'
    change = input(f'Okay, who should I change the {part} to?')
    bookie = models.session.query(models.Book).filter_by(part=toedit)
    for bkie in bookie:
        bkie.part = change

This does not work because a variable cant be passed as an attribute. Or can it? Is there any way to make this work?

1 Answer

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,441 Points

Hey Andy McDonald, excellent question!

Using star notation, a dict can be used as keyword/pairs. If using part as a variable, the dict can be defined then expanded as kwargs.

# define
kwdict = {part: toedit}

# use as
bookie = models.session.query(models.Book).filter_by(**kwdict)

# then 
    for bkie in bookie:
        setattr(bkie, part, change)

Post back if you need more help. Good luck!!!

Andy McDonald
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Andy McDonald
Python Development Techdegree Graduate 13,801 Points

Unless theres something I'm missing. I dont think this is valid syntax. I need to pass this as(attribute = 'value'}. Passing a key value pair would be more like ('key' : 'pair')

Chris Freeman
Chris Freeman
Treehouse Moderator 68,441 Points

I've created the following code. Save to a local file and run using python <filename>. It may help understand using the "star notation" to unpack a dictionary into attributes.

import datetime


class def_init:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

    def __str__(self):
        return f"my attributes are: {self.__dict__}"

part = "pub_date"
value1 = datetime.datetime.now()

kwarg_dict = {part: value1}

# line below shows that a dict will expand to
# key becoming the attribute and value becoming the attribute value
# **{part1: value1} becomes (pub_date=<datetime_object>)
print("\nCreating new instance using star notation for dict to kwargs")
print(f"kwargs are {kwarg_dict}")
an_instance = def_init(**kwarg_dict)
print(an_instance)
print(f"check attr pub_date: {an_instance.pub_date}")

outputs:

$ python dict_init.py 

Creating new instance using star notation for dict to kwargs
kwargs are {'pub_date': datetime.datetime(2021, 8, 6, 23, 14, 6, 941012)}
my attributes are: {'pub_date': datetime.datetime(2021, 8, 6, 23, 14, 6, 941012)}
check attr pub_date: 2021-08-06 23:14:06.941012
Andy McDonald
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Andy McDonald
Python Development Techdegree Graduate 13,801 Points

I just realized how to use this and this next question is totally hypothetical: Lets say there's lots of dot notation stacked onto one object. How would you use setattr to do that for the last attribute? Is it just each attribute has its own positional argument with the last argument being what it is set equal to?

Chris Freeman
Chris Freeman
Treehouse Moderator 68,441 Points

Regarding setattr use, here’s some other examples:

class ThirdLevel:
    def __init__(self):
        self.color = "red"
        self.name = "3Level"

    def __str__(self):
        return self.name


class SecondLevel:
    def __init__(self):
        self.s_attr = ThirdLevel()
        self.name = "2Level"

    def __str__(self):
        return self.name


class TopLevel:
    def __init__(self):
        self.t_attr = SecondLevel()
        self.name = "TLevel"

    def __str__(self):
        return self.name

t = TopLevel()
print("1", t, t.t_attr, t.t_attr.s_attr, t.t_attr.s_attr.color)

target = t
setattr(target.t_attr.s_attr, "color", "blue")
print("2", t, t.t_attr, t.t_attr.s_attr, t.t_attr.s_attr.color)

deep = t.t_attr.s_attr
setattr(deep, "color", "green")
print("3", t, t.t_attr, t.t_attr.s_attr, t.t_attr.s_attr.color)

Outputs:

1 TLevel 2Level 3Level red
2 TLevel 2Level 3Level blue
3 TLevel 2Level 3Level green

Play around with this. Post back if you need more help. Good luck!!!