Python Forum

Full Version: Help creating a class instance dynamically
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hey folks,

I've been trying to dump the contents of my google drive sheet into class instances. My understanding of classes and object oriented programming in python feels a little bit incomplete so if I misuse any terminology feel free to correct me at any point here.

The output of the drive sheet spits the contents into a dictionary contained inside of a list. This is the output here

This is my class here:
class Turret:
    def __init__(self, copyable, turret_name, burst, fire_rate, hull_dmg, shield_dmg, sound_name, random, man_accuracy, life_time, laser_thickness, speed, burst_time_in_between, price, color):
        self.copyable = copyable
        self.turret_name = turret_name
        self.burst = burst
        self.fire_rate = fire_rate
        self.hull_damage = hull_dmg
        self.shield_damage = shield_dmg
        self.sound_name = sound_name
        self.random = random
        self.man_accuracy = man_accuracy
        self.life_time = life_time
        self.laser_thickness = laser_thickness
        self.speed = speed
        self.burst_time_in_between = burst_time_in_between
        self.price = price
        self.color = color
For the class object/instance name I am trying to use the TurretName value from the sheet output, but I have been unable to do this successfully.

To convert the raw output of the drive sheet into a readable format, I have been using this ugly and inefficient function here.
# Take turret sheet output and log it's contents as objects under the Turret class.
def clean_turret_sheet_data(currentTurrets):
    i = 0
    b = 0
    selected_item_list = []
    for dict in currentTurrets:
        selected_turret = currentTurrets[i]
        for key, value in selected_turret.items():
            temp = [key, value]
            selected_item_list.append(temp)
        copyable = selected_item_list[b][1]
        b += 1
        turret_name = selected_item_list[b][1]
        b += 1
        burst = selected_item_list[b][1]
        b += 1
        fire_rate = selected_item_list[b][1]
        b += 1
        hull_dmg = selected_item_list[b][1]
        b += 1
        shield_dmg = selected_item_list[b][1]
        b += 1
        sound_name = selected_item_list[b][1]
        b += 1
        random = selected_item_list[b][1]
        b += 1
        man_accuracy = selected_item_list[b][1]
        b += 1
        life_time = selected_item_list[b][1]
        b += 1
        laser_thickness = selected_item_list[b][1]
        b += 1
        speed = selected_item_list[b][1]
        b += 1
        burst_time_in_between = selected_item_list[b][1]
        b += 2
        price = selected_item_list[b][1]
        b += 2
        color= selected_item_list[b][1]
        b += 1
        turretList[i] = Turret(copyable, turret_name, burst, fire_rate, hull_dmg, shield_dmg, sound_name, random, man_accuracy, life_time, laser_thickness, speed, burst_time_in_between, price, color)
        print(turretList[i].turret_name, "logged!")
        i += 1
It seems like the class instance has to be defined before the script can be run? My question is: is there any way to initiate the class using external variables from outside the code after the script has been run.

TLDR: I am trying to create a class by calling an specific list item that doesn't exist until the script is run but I don't know how to do it and I cant find how online.
Your code scare me.

Example. You can try something like this.
class Turret:
    pass

def clean_turret_sheet_data(currentTurrets):
    turret_list = []
    for turret in currentTurrets:
        new_turret = Turret()
        for key, value in turret.items():
            setattr(new_turret, key, value)

        turret_list.append(new_turret)
    return turret_list
You can do even shorter:
class Turret:
    def __init__(self, attrdict):
        self.__dict__.update(attrdict)

def clean_turret_sheet_data(currentTurrets):
    return [Turret(turret) for turret in currentTurrets]
I suggest using a namedtuple object for better control
from collections import namedtuple

_Turret = namedtuple('Turret', """
    copyable, turret_name, burst,
    fire_rate, hull_dmg, shield_dmg,
    sound_name, random, man_accuracy,
    life_time, laser_thickness,
    speed, burst_time_in_between,
    price, color""")


class Turret(_Turret):
    __slots__ = ()

def clean_turret_sheet_data(currentTurrets):
    return [Turret(**t) for t in currentTurrets]
    # or [Turret(*t.values()) for t in currentTurrets] if attribute names are changed from the initial data.
Hey guys thanks for the replies, these suggestions really are helping me improve my skills.

Unfortunately I don't think these changes would solve my issue (probably my own fault for explaining poorly).

The problem is that since the instances are created when the code is run, I am unable to call them and their attributes in the code since the interpreter doesnt think they exist (I think).

So for example, even after defining all of that, if I try to do something like

print(SmallSuppressor.burst)
I get the error "NameError: name 'SmallSuppressor' is not defined".
class Turret:
    pass

def clean_turret_sheet_data(currentTurrets):
    for turret in currentTurrets:
        new_turret = Turret()
        for key, value in turret.items():
            setattr(new_turret, key, value)

        setattr(Turret, turret['TurretName'], new_turret)

clean_turret_sheet_data(arg)
print(Turret.SmallSuppressor.burst)
You could use the code Gribouillis provided and return a dictionary instead:

def clean_turret_sheet_data(currentTurrets):
    turrets = [Turret(**t) for t in currentTurrets]
    return {turret.turret_name: turret for turret in turrets}

turret_data = clean_turret_sheet_data(currentTurrets)
print(turret_data['SmallSuppressor'].burst)
Here is a complete working code. I assume that SHEET_DATA is the list that you linked
from collections import namedtuple
import re
 
def to_underscore(word):
    return '_'.join(x.lower() for x in re.findall(r'[A-Z]+[a-z]*', word))


def clean_turret_sheet_data(currentTurrets):

    _Turret = namedtuple('Turret',
        [to_underscore(k) for k in currentTurrets[0].keys()])

    class Turret(_Turret):
        __slots__ = ()
        
        def is_fast(self):
            return self.speed > 500
    
    instances = [
        Turret(**dict((to_underscore(k), v) for k, v in t.items()))
        for t in currentTurrets]
    
    return dict((t.turret_name, t) for t in instances)

turret_by_name = clean_turret_sheet_data(SHEET_DATA)

print(turret_by_name.keys())
print(turret_by_name['SmallSuppressor'].burst)
print('SmallSuppressor is fast:', turret_by_name['SmallSuppressor'].is_fast())
The output is
Output:
dict_keys(['MediumArbiter', 'MissileTitan', 'MediumBoltor', 'MediumHarpoon', 'BeamPrometheus', 'FighterTorpedo', 'TurretTitan', 'TachyonPrometheus', 'LargePrometheus', 'SmallFury', 'SmallSuppressor', 'LargeAssimilator', 'BomberTurret']) 1 SmallSuppressor is fast: True
I keep getting an error

"line 48
def get_turret_sheet_data():
^
SyntaxError: invalid syntax"

and im not sure why, I dont understand this code at all really lol

Line 48 would be

def to_underscore(word):
    return '_'.join(x.lower() for x in re.findall(r'[A-Z]+[a-z]*', word))
If you copy and paste the whole code as I wrote it, there is no syntax error (double click the code to select it). You only need to add at the beginning
SHEET_DATA = [{...}, {...}, ...] # the list you provided in hastebin