-1

I am trying to learn Python and while creating my own class, I came across this problem. I want to call an inner function inside my function. The problem is how can I build this type of structure in Python 3?

    def GetAttributes(self):
        def GetName(self):
            return self.name
        def GetSurname(self):
            return self.surname
        def GetAge(self):
            return self.age
        def GetID(self):
            return self.ID

Basicly I want to be able to do this "GetAttributes().GetName()" which will return self.name or "GetAttributes().GetAge()" which will return self.

Edit: For duplicate of another question. I am trying to call multiple functions not one.

umutfirat
  • 133
  • 1
  • 7
  • 6
    Why do you want to wrap these functions in another one? Not a good style – user8408080 Dec 04 '18 at 13:23
  • 6
    In Python it is much preferred to directly access the attributes instead of preemtively creating getters and setters for each. You can always turn a simple attribute into a more complex thing using `property` later. – Graipher Dec 04 '18 at 13:24
  • Possible duplicate of [Call a function defined in another function](https://stackoverflow.com/questions/8457669/call-a-function-defined-in-another-function) – Georgy Dec 04 '18 at 13:25
  • Perhaps you would like to stick to Python naming conventions, which have method names in lower case. – khelwood Dec 04 '18 at 13:26
  • That is why I asked. I wanted to build this structure but I don't know what is wrong and why it is not a good style etc. Lastly I will keep this Python Naming Conventions in my mind. – umutfirat Dec 04 '18 at 13:37

3 Answers3

2

I think your solution is simple, apply OOP in Python, and rather than making GetAttributes as function make it as a class.

class GetAttributes(object):
    def __init__(self, name, surname, age, custome_id):
        self.name = name
        self.surname = surname
        self.age = age
        self.ID = custome_id
    def GetName(self):
        return self.name
    def GetSurname(self):
        return self.surname
    def GetAge(self):
        return self.age
    def GetID(self):
        return self.ID

Output

>>> ga = GetAttributes("Shivam", "Kotwalia", 25, "N15T5")
>>> ga.GetName()
    'Shivam'
>>> ga.GetAge()
    25

I am not very much sure that how are you filling the data, but this is seems to be very pythonic way tto do this :)

Happy Pythoning!

Shivam Kotwalia
  • 1,419
  • 2
  • 15
  • 20
  • Yes I could have done that but wanted to know if I try my structure and it did not work. Wanted to know why. Thanks for the solution – umutfirat Dec 04 '18 at 13:41
  • The reason your original idea doesn't work is because `GetName()` and such are defined as a function within the local scope of the main function `GetAttributes()`. They are *not* the attribute of `GetAttributes()` so you can't reference them by ways of `GetAttributes().GetName()`. Seeing as how you are using `self` as a parameter though you are definitely thinking of using a `class` structure like @ShivamKotwalia is suggesting. – r.ook Dec 04 '18 at 13:55
  • So sorry, couldn't retrace your idea. But now I get the idea you can definitely then use - Regarding the why - because what you are trying to achieve is a "OOP approach" and you are doing through functions, secondly the function can only return what has been specified to return. More at - https://docs.python.org/3/howto/functional.html?highlight=functions – Shivam Kotwalia Dec 04 '18 at 13:57
2

@ShivamKotwalia's answer is what you're looking for, but since it seems you are trying to understand why your implementation didn't work, I'll expand a little bit.

Currently your implementation is creating nested functions in the local scope. In another word, once the code is outside of the function the objects inside the local scope cease to exist unless returned to the outer scope. Take for instance a modification of your code:

def GetAttributes(person):
    def GetName(person):
        return person.name
    def GetSurname(person):
        return person.surname
    return GetName, GetSurname

This allows you to reference back to GetName and GetSurname now because they have been passed back to the outer scope. Assuming you have a Bob object that has the attribute name and surname:

name = GetAttributes(Bob)
name
# (<function GetAttributes.<locals>.GetName at 0x03E9AB28>, <function GetAttributes.<locals>.GetSurname at 0x03E9AB70>)

Notice how name has the two inner functions passed in, and that they are <locals>. Whereas GetAttributes is under the global scope: <function GetAttributes at 0x03E9AA50>.

You can then get the two functions by the index of name:

name[0](Bob)
# Bobby
name[1](Bob)
# Tables

Notice again how you still require Bob to be passed in, because both functions were defined as requiring the argument person. You could opt to remove the arguments...:

def GetAttributes(person):
    def GetName():
        return person.name
    def GetSurname():
        return person.surname
    return GetName, GetSurname

But then you're still faced with a few problems:

  1. You first have to assign an object for the function to access the inner functions.
  2. The GetAttributes function return nothing but its inner functions.
  3. The access to the local functions are not meaningfully structured.
  4. There's no guarantee that the object you pass in will always have name and surname attributes.

That's why the OOP approach (e.g. class) and methods exist:

class Person:
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname
    def GetName(self):
        return self.name
    def GetSurname(self):
        return self.surname

This address all the issues above as you now have:

  1. You just need to create an instance of the Person class instead of messy function handling.
  2. GetAttributes is not necessary any more.
  3. Easy and meaningful access to get Person().GetName().
  4. The objects using these methods are guaranteed to have name and surname attributes.

"But what about the GetAttributes method?" you ask? Why, Python does have a built-in getattr() function that will serve your need!

getattr(Bob, 'name')
# Bobby

Having said all that, there does exist function attributes where you can do something like this:

def GetAttributes(person):
    def GetName():
        return person.name
    def GetSurname():
        return person.surname
    GetAttributes.GetName = GetName
    GetAttributes.GetSurname = GetSurname
    return GetAttributes

GetAttributes(Bob).GetName()
# Bobby

But you very quickly run into limitations and problems managing all the objects and functions you might pass within the scopes. Try it and you will see. Thus, why would you opt to go through this trouble when class, getattr and methods already do everything that you need with the benefits of safeguards in place?

r.ook
  • 13,466
  • 2
  • 22
  • 39
  • That's what they say in Zen - Simple is better than complex. Complex is better than complicated. – Shivam Kotwalia Dec 04 '18 at 16:34
  • Did not see this "Thus, why would you opt to go through this trouble when class, getattr and methods already do everything that you need with the benefits of safeguards in place?" at first. I asked myself these questions: What if I have too much attributes? Can I pass these in one function to get datas from a organized way? Would it be more readable? Thats why I asked if it viable – umutfirat Dec 04 '18 at 18:09
  • It's definitely more readable and organized doing it under the `class` structure, thus why you don't see a lot of people using function attributes (some might even consider it a hack). Don't get the intention of my answer wrong - while you can, doesn't mean you should. – r.ook Dec 04 '18 at 20:37
1

I think this might be a little helpful, Try this:

def get_attributes(self, attr_name):
    return {
        'name': self.name,
        'surname': self.surname,
        'age': self.age,
        'ID': self.ID,
    }.get(attr_name)

and you can use it like get_attributes(name) or get_attributes(age) and so on. also, note that in the comments our friends are saying method names are better to be lower case in Python.it is one of Python naming conventions.

Mehrdad Pedramfar
  • 10,941
  • 4
  • 38
  • 59