-1

I'm a python newbie, and I'm trying to create a function that returns an instance of one of three classes, using the arguments of the function as attributes of the instance.

class User:
    def __init__(self, nick, full_name, reg_date, age, role):
        self.nick = nick
        self.full_name = full_name 
        self.reg_date = reg_date
        self.age = age
        self.role = role

class Staff(User):
    def __init__(self, nick, full_name, reg_date, age, role):
        super().__init__(nick, full_name, reg_date, age, role)


class Adult(Staff):
    def __init__(self, nick, full_name, reg_date, age, role):
        super().__init__(nick, full_name, reg_date, age, role)

class Young(Adult):
    def __init__(self,nick, full_name, reg_date, age, role):
        super().__init__(full_name, nick, reg_date, age, role)



def sign_up(nick, full_name, reg_date, age, role):
    nick = (input('Choose a nickname: '))
    full_name = (input('Please, introduce your name: '))
    reg_date = str(date.today())
    age = (input('Please, introduce your age: '))
    role = (input('Please, introduce your role (Staff, Adult or Young)'))
    valid_roles = ['Staff', 'Adult', 'Young']
    exec('nick = role(full_name, reg_date, age, role)

My goal is that when you run the sign in function the value the user introduces into "nick" argument will be the instance name, role the class of the instancem, and the other arguments work as the instance.

for example:

sign_in('nick1', 'John Doe', reg_date, 38, 'Staff')

Should result in this:

nick1 = Staff(full_name, reg_date, age)

Is my first question here in Stack Overflow and I hope I explained myself clearly. Thanks.

Michael Szczesny
  • 4,911
  • 5
  • 15
  • 32
Arochal
  • 3
  • 1
  • 3
    Your function doesn't really make sense. You are passing arguments, but then, you immediately use `input` to overwrite most of the values. – juanpa.arrivillaga Jun 28 '22 at 19:08
  • 5
    Also, don't sue `exec` here. – juanpa.arrivillaga Jun 28 '22 at 19:08
  • 3
    Just don't use `exec` in general... *especially* where user input is involved... – BeRT2me Jun 28 '22 at 19:09
  • 1
    Did you mean for those three other classes to all derive from User? – Kenny Ostrom Jun 28 '22 at 19:10
  • 4
    Your class clearly uses expects `nick` as an argument. You shouldn't be trying to name a variable using the string value of `nick`. Just return the new instance, let the caller determine what to store it to. Probably a `dict` or the like (if `nick` is required to be unique, the caller can extract it and use it as the key, with the instance as the value). – ShadowRanger Jun 28 '22 at 19:10
  • 2
    you can stick the classes in a dict and lookup the right one based on your string. – The Fool Jun 28 '22 at 19:11
  • 2
    It's also a little weird that the classes all accept `role` as an argument, but the class hierarchy itself *defines* specific roles by the specified class. You ought to choose, either `role` is an attribute or it's embedded in the class hierarchy. In this case, I'd suggest attribute; alll `Young` are not `Adult`s, all `Adult`s are not `Staff`, etc. Inheritance makes no sense here. – ShadowRanger Jun 28 '22 at 19:13
  • 1
    Lots of valid points so far. I'll add to the pile: dynamically created variables are never a good idea. Ever. Either use a dictionary to add new instances based on the person's name, role, and maybe age as a tuple, or simply return a single instance from the function, and collect the instances in a simple list. – ddejohn Jun 28 '22 at 19:18
  • Beside the point, but passthrough `__init__`s aren't necessary. The child classes will inherit `__init__` from their parent. And for that matter, it's actually hurting you here since you reversed `nick, full_name` in `Young.__init__` accidentally. – wjandrea Jun 28 '22 at 19:23
  • @wjandrea I'm not sure I follow what you mean by "passthrough" inits. Is there another way to instantiate the parent class? – ddejohn Jun 28 '22 at 19:27
  • @ddejohn I mean the inits OP wrote don't do anything except take the same arguments as the parent init and forward them on to the parent init. There's no point doing that when they could simply use the inherited init. – wjandrea Jun 28 '22 at 19:29
  • What I mean is, how do you actually use the inherited init, other than by calling `super()`? – ddejohn Jun 28 '22 at 19:36
  • 2
    @ddejohn see my answer. If it doesn't have its own init, it'll default to the inherited init. – BeRT2me Jun 28 '22 at 19:37
  • Ahhh, okay, I was confused because I've never actually seen that before. I did not realize that Python will look up the parent's init when instantiating a class. – ddejohn Jun 28 '22 at 19:40
  • Partial duplicate: [How do I create variable variables?](/q/1373164/4518341) (TLDR: Don't; use a dict instead.) – wjandrea Jun 28 '22 at 19:42
  • @ddejohn, it will look every method up, in the parent if the child doesnt implement it. Thats the whole point of inheritance. As soon as you implement a method, including init, it will not be looked up in the parent, therefore you oftentimes call the parent init from the child init, if you need to do some additional things. – The Fool Jul 01 '22 at 07:36

1 Answers1

0

Don't arbitrarily make variables. Use something like a dictionary instead.

class User:
    def __init__(self, nick, full_name, reg_date, age, role):
        self.nick = nick
        self.full_name = full_name 
        self.reg_date = reg_date
        self.age = age
        self.role = role

class Staff(User):
    pass

class Adult(Staff):
    pass

class Young(Adult):
    pass

def sign_up(nick, full_name, reg_date, age, role):
    user = {}
    if role == 'Staff':
        user[nick] = Staff(nick, full_name, reg_date, age, role)
    elif role == 'Adult':
        user[nick] = Adult(nick, full_name, reg_date, age, role)
    elif role == 'Young':
        user[nick] = Young(nick, full_name, reg_date, age, role)
    return user

users = {}

users |= sign_up('nick1', 'John Doe', 'today', 38, 'Staff')
users |= sign_up('nick2', 'Jane Doe', 'yesterday', 40, 'Adult')
print(users)

Output:

{'nick1': <__main__.Staff object at 0x1009d2c80>,
 'nick2': <__main__.Adult object at 0x100ae1b10>}
BeRT2me
  • 12,699
  • 2
  • 13
  • 31
  • Also, follow the good advice in the question's comment on how to improve your class structure, all I did was make it functional. – BeRT2me Jun 28 '22 at 19:19
  • 1
    `global` is not necessary here. But also, you shouldn't be mutating a global dictionary to begin with. *return a new dictionary* from the function and let the *caller decide* what to do with it. (I didn't downvote, btw) – juanpa.arrivillaga Jun 28 '22 at 19:19
  • In this case, where the class constructors all have the same parameters, you could factor out the if-statement like this: `available_roles = {cls.__name__: cls for cls in [Staff, Adult, Young]}; cls = available_roles[role]; user[nick] = cls(nick, ...)` – wjandrea Jun 28 '22 at 19:48
  • @wjandrea feel free to make the edit, as I'm not certain how that works~ – BeRT2me Jun 28 '22 at 19:51
  • 1
    Well... it'd be a substantial change that'd require explanation. So [here's a gist](https://gist.github.com/wjandrea/92c5d0fff3e1ee2d57d4cd7c7585a407), and you can incorporate it if you like. – wjandrea Jun 28 '22 at 19:56