Modifying Function Arguments in Python to Access Private Fields Within a Class - python

I'm trying to create a Class with a list of fields. See code below.
class Character:
# Private Fields:
__age = 18
__weight = 200
__height = 72
def __init__(self, name):
self.__name = name
#property
def get_age(self):
return self.__age
#property
def get_name(self):
return self.__name
#property
def get_weight(self):
return self.__weight
#property
def get_height(self):
return self.__height
person = Character("someone")
print("name =", person.get_name,",", "age =", person.get_age)
Is there a way to avoid writing the #property for every private field you want to access? For instance is there a way to pass an attribute into a more general getter function like:
def get_attr(self,attr):
#set attr to __attr
#return self.attr
I tried using the join function, but it didn't work
Thanks for any help

To answer the question as asked, the simple solution is to compensate for the name mangling that is done with private members. e.g. to get the __age attribute you'd use: person._Character__age
But, this would be a terrible idea and I wouldn't recommend it. If you need them to be easily accessible, just remove the underscores. If they really need to be private, they shouldn't be easily accessible from outside the class anyway, so putting in a way to make them accessible defeats the purpose.

Related

Python letting me access Protected instance attributes outside of class

In this Python program, I created an Employee class which has a protected instance attribute, '_id'. It is my understanding that in Python, to create a protected attribute, you start the name with a single underscore. However, it is allowing me to access this protected attribute outside of the class. The output of this program is:
12345
Why is it doing this?
class Employee:
def __init__(self, full_name, ID = 0):
self._firstname, self._lastname = full_name.split(" ")
self._id = ID
#property
def firstname(self):
return self._firstname
#property
def id(self):
return self._id
#firstname.setter
def firstname(self, new):
self._firstname = new
#id.setter
def id(self, new):
self._id = new
#firstname.deleter
def firstname(self):
self._firstname = None
#id.deleter
def id(self):
_id = None
bob = Employee("Billy Bob", 12345)
print(bob._id)
Thank you. I understand now that the underscores are simply convention. I was confused to thinking that access modifiers existed because when I used the convention for a private attribute, '__', it wouldn't let me access the attribute externally. The reason it wouldn't let me access it was because of name mangling.

#property decorator of python not working

I was trying to set some property to a class via decorator but its not working as expected. How can I get the age via property decorator.
class Person:
def __init__(self):
self.name = ""
self.age = ""
self.dob = ""
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
#property
def age(self):
return self._age
#age.setter
def age(self, value):
self._age = value
#property
def dob(self):
return self._dob
#dob.setter
def dob(self, value):
self._dob = value
self._age = 20 #Utility.getAge(value)
if __name__ == '__main__':
p = Person()
p.name = "Andrew"
p.dob = "10-10-1980"
print p.name
print p.dob
print p.age
Output:
John
10-10-1980
#20 <-missing
I am not getting the age. Am I missing something?
Ok, this took me a while to actually find out why the above code was not working in python 2.7.
If you look at the property documentation for python2.7, you would find that the class that has the property decorators used is actually inheriting object class and your code doesn't.
Now, when you don't inherit, the property decorator actually doesn't work and setting or getting properties don't work either
(Put a print statements in getter or setter functions and they wont be printed since they were never invoked while setting p.name or getting p.name).
Question : So how come get/set for p.name and p.dob works?
Since, you are not inheriting object class in your class, the property decorators are useless, they are not being invoked but have created those property on the Person object.
But, when you use below code, you are explicitly setting those value (without the use of setters), hence thy are printed and p.age never got assigned any value.
p.name = "Andrew"
p.dob = "10-10-1980"
Code Fix : Update your class declaration to -->
class Person(object):
and setters/getters would work (check using print statements) and self.age would also work.
Bonus : Python3 onwards, all classes, by default, inherit object class.

Python RPG Inheritance Issues?

I am running into a TypeError for my super().init method stating it only takes one positional argument and three are given. I assumed that the Enchanted class inherited the other parameters from the parent classes Weapons and Item, but I seemed to have missed something?
Using python 3.5 and link to the GitHub repository if needed is here: PythonRPG.
#base item class
class Item(object):
def __init__(self, name, description):
self.name = name
self.description = description
def __init__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description)
#start weapons
class Weapons(Item):
def __init__(self, name, description, attack):
self.attack = attack
super().__init__(name, description)
def __str__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description, self.attack)
class Enchanted(Weapons):
def __init__(self):
#error appears here
super().__init__(name="Enchanted Longsword", description="A prestine longsword you found with small runes inscribed along the Cross Guard. You feel a small magical force emanating from the weapon as you hold it.", attack = 12)
You have two __init__ methods in your Item class. The second overwrites the first, and since it takes only one positional parameter (self), the error is thrown. Simple fix: get rid of the second __init__.
I'm not certain, but perhaps you meant your second __init__ to be __str__?
class Item(object):
def __init__(self, name, description):
self.name = name
self.description = description
def __str__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description)
A nice answer is already provided by jme. I am guessing that you are trying something like method overloading or creating different constructors for Item class.
In a python class you can not have two methods of same name. But you can achieve this property by providing default values to the method parameters. Something like this:
class Item(object):
def __init__(self, name=None, description=None):
self.name = name
self.description = description

Python: how to print instance variable of type string

I am trying to print a string variable returned by name() function, which in this case should print "Jim, but Python is printing
`<bound method Human.name of <__main__.Human object at 0x7f9a18e2aed0>>`
Below is the code.
class Human:
def __init__(self):
name = None
def setName(self, _name):
name = _name
def name(self):
return self.name
jim = Human()
jim.setName("Jim")
print(jim.name())
UPDATE:
After reading the answers, i updated the code as shown below, but, now i am getting a new error TypeError: 'str' object is not callable
class Human:
def __init__(self):
self.name = None
def setName(self, _name):
self.name = _name
def name(self):
return self.name
jim = Human()
jim.setName("Jim")
print(jim.name())
self.name is the method itself. You have no attributes storing the name. Nowhere do you actually set the name as an attribute. The following works:
class Human:
def __init__(self):
self.name = None
def setName(self, _name):
self.name = _name
# NOTE: There is no more name method here!
Now you have an actual attribute, and you don't need to call the method here:
jim = Human()
jim.setName("Jim")
print(jim.name) # directly using the attribute
You could even just set the attribute directly:
jim = Human()
jim.name = "Jim"
print(jim.name)
Alternatively, use self._name to store the name on the instance:
class Human:
_name = None
def setName(self, _name):
self._name = _name
def name(self):
return self._name
Here we used a class attribute Human._name as a default, and only set self._name on the instance in the Human.setName() method.
The problem is that name is the name of the internal variable in your object and also the name of the method.
The namespace for variables and methods is the same. Change the name of your method to something other than name. This will fix your getter. On first glance I thought that that would be all you have to do, but the recommendation in Martijn's answer also applies -- you need to assign to self.name and not just name in order to get your setter to work as well.
As an aside, this getter/setter pattern is not usually appropriate for Python. You should ask yourself why you want to use a getter/setter pattern over simply accessing the object's variable directly. See the section on getters and setters in this article for more detail.
You can use setter and getter properties instead of your custom defined methods.
class Human():
def __init__(self):
self._name = None
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
And then, use them:
jim = Human()
jim.name = "Jim"
print(jim.name)

Creating objects from static properties in python

I have a Category class which has different names for each categories, the names of the categories can be unknown, good and bad, all categories share the same behavior so i don't want to create sub classes for each type of category, the problem comes when i am trying to
create the different categories in this way:
Category.GOOD
This statement should return a category object with his name setting to 'good' so i try
the following:
class Category(object):
def __init__(self, name):
self.name = name
#property
def GOOD(self):
category = Category(name='good')
return category
#property
def BAD(self):
category = Category(name='bad')
return category
Then i created and use the category with the following output:
c = Category.GOOD
c.name
AttributeError: 'property' object has no attribute 'name'
Realizing that this doesn't work i try a java like approach:
class Category(object):
GOOD = Category(name='good')
BAD = Category(name='bad')
def __init__(self, name):
self.name = name
What i get here is a undefined name "Category" error, so my question is if there is a pythonic way to create a category object like this.
You probably want to use classmethods:
class Category(object):
#classmethod
def GOOD(cls):
category = cls(name='GOOD')
return category
Now you can do c = Category.GOOD().
You cannot do this with a property; you either have to use a classmethod, or create your own descriptor for that:
class classproperty(property):
def __get__(self, inst, cls):
return self.fget(cls)
I'm abusing the property decorator here; it implements __set__ and __del__ as well, but we can just ignore those here for convenience sake.
Then use that instead of property:
class Category(object):
def __init__(self, name):
self.name = name
#classproperty
def GOOD(cls):
return cls(name='good')
#classproperty
def BAD(cls):
return cls(name='bad')
Now accessing Category.GOOD works:
>>> Category.GOOD
<__main__.Category object at 0x10f49df50>
>>> Category.GOOD.name
'good'
I'd use module variables for this. Consider you have the module category.py:
class Category(object):
# stuff...
now you put the two global objects in it:
GOOD = Category(name='good')
BAD = Category(name='bad')
You can use it like that:
from path.to.category import GOOD, BAD
I don't say that this is pythonic but I think this approach is elegant.
The main point that you could not use class definition inside that class definition itself. So the most straight way to achieve what you are want is to use class/static methods as shown below, or even package constants.
class Category(object):
def __init__(self, name):
self.name = name
#classmethod
def GOOD(cls):
return Category(name='good')
#classmethod
def BAD(cls):
return Category(name='bad')
print Category.GOOD().name
or
class Category(object):
def __init__(self, name):
self.name = name
#staticmethod
def GOOD():
return Category(name='good')
#staticmethod
def BAD():
return Category(name='bad')
print Category.GOOD().name

Categories