I have an object which inherits from ndb.Model (a Google App Engine thing). This object has a property called commentid:
class Comment(ndb.Model):
commentid = ndb.StringProperty()
Reading a bunch of articles, they all say this is the way to implement a property:
#property
def commentid(self):
if not self._commentid:
self._commentid = "1"
return self._commentid
but I get an error saying Comment object has no attribute _commentid. What am I doing wrong?
Edit: Ok obviously I'm a bit confused here. I come from Objective-C, where if you have a property called x then you automatically get a variable called _x in your getters and setters. So I thought this is what was happening here in Python too. But apparently I need to manually set a value for the variable with an underscore prefix.
All I want is to implement a getter where I do some checking of the value before returning it. How would I do this?
Implementing a property like that requires you to define the attribute for your object. What you're doing there, is defining a class called Comment but you don't define any attributes for it's objects, you define them for the class itself.
Let me demonstrate with a small example:
class ExampleClass:
name = "Example Object"
a = ExampleClass() # Init new instance of ExampleClass
print(a.name) # a doesn't own an attribute called "name"
print(ExampleClass.name) # --> "Example Object"
In the above example, I define class ExampleClass and give it a variable name with a value Example Object. After that, I create an object a = ExampleClass(), however it does not get the name attribute, cause the attribute is defined for the class itself, not for it's objects.
To fix this problem, you define the name inside __init__ -method, which gets called whenever an object of that class is created.
class ExampleClass:
def __init__(self):
self.name = "Example Class"
a = ExampleClass() # Init new instance of ExampleClass
print(a.name) # --> "Example Class"
print(ExampleClass.name) # --> ERROR: Exampleclass.name doesn't exist
There I define the ExampleClass again, but I also define __init__ method for it. Init method takes only one parameter, self, which will be automatically given to the function. It's the object which is being created. Then I set self.name = "Example Class", and since self is the object itself, we set the object's attribute name.
Creating the property
To implement setter and getter for your attribute, you add the following:
class ExampleClass:
def __init__(self):
self.name = "Example Class"
#property
def name(self):
if not self._name:
pass #blabla code here
return self._name
#name.setter
def name(self, value):
#blabla more code
self._name = value
Also, you should edit the __init__ method to take name as a parameter too.
def __init__(self, name="Example Object"):
self.name = name
If you access self._commentid directly, it needs to be defined or it'll raise an exception. Since you're instead checking if _commentid is defined at all (to give it a default value), I'd use hasattr:
#property
def commentid(self):
if not hasattr(self, "_commentid"):
self._commentid = "1"
return self._commentid
Related
Can you please explain to me what is the difference between the attribute that we put in def __init__ (c_name) and the one we put inside the class (self.name)?
class human:
def __init__(self, c_name, c_age):
print("Creation of Human...")
self.name = c_name
self.age = c_age
c_name is a parameter of the function __init__ and __init__ is called whenever a new instance of that class is created. In Python self also represents an instance of the class. So here, doing self.name = c_name assigns the local value of c_name to the attribute self.name. This means it will be accessible for the life of the object instead of just inside the call to __init__
when we use attribute in __init__ function
the init function automatically get involved during the the object creation
anything we create using self. refers to the class attribute
according to the question self.name is attribute specific to the the object created from class human.
we can access this attribute from anywhere like this
a = human("anyname",11)
print (a.name)
output
anyname
when we use a function with some other name
First, c_name is not an attribute. It's called "parameter".
self.name is an instance variable. Because self is pointing to our recently created instance and we set the attribute name to it.
c_name is local to the __init__ method, so if we don't store it in our instance namespace, we can't have access to it inside another methods or elsewhere outside __init__. Just like regular functions:
def fn(a):
a += 1
fn(10)
print(a) # NameError: name 'a' is not defined
I have class attributes in my class, that I want to set dynamically, here's a code example
class Something:
attribute1 = 42 # this is shared between all class instances
def _init_(self, value):
self.value = value
my question is: Is there a way to set that class attribute (attribute1) to some value, the same way that I can set my object instance attributes like this:
something = Something(value)
Yes just do
Something.attribute1 = "some value"
Class attributes can be accessed via the class name. You can do this inside any function defined in the class or even outside of it.
In below example Test class has two instance method and one classmethod
In set_cls_var_1 method I set class variable using self.
In set_cls_var_2 method I call class method using self.
class Test():
#class variable
cls_var = 10
def __init__(self):
obj_var=20
def set_cls_var_1(self,val):
#second method to access class variable
print "first "
self.cls_var = val
def set_cls_var_2(self):
print "second"
self.task(200)
#classmethod
def task(cls,val):
cls.cls_var = val
t=Test()
#set class variable by first method
t.set_cls_var_1(100)
print Test.cls_var
#set class variable by second method
t.set_cls_var_2()
print Test.cls_var
Output
first
10
second
200
Expected Output
first
100
second
200
My question is:
why only classmethod can call by self, Why not class variable
When you attempt to access an object's attribute using self, Python first searches the object's attributes. If it cannot find it there, then is searches the object's class's attributes. That is what's happening in your case;
Python first searches t's attributes. It doesn't find cls_var, so it then searches the T class's attributes. It finds cls_var so it stops, and returns cls_var's value.
However, when assigning attributes to self, Python always assigns them directly to the object, and never the object's class unless explicitly told to do so. That's why assinging self.cls_var to 100 didn't affect Test's cls_var attrbiute.
I find something else that always use following way to access classmethod or variable in instance method
class Test():
#class variable
cls_var = 10
def __init__(self):
obj_var=20
def set_cls_var_1(self,val):
#first method to access class variable
print "first type"
cls = self.__class__
cls.cls_var = val
t=Test()
#set class variable by first method
t.set_cls_var_1(100)
print Test.cls_var
When defining the Test class like you did, python creates a class object called Test which has an attribute cls_var equal to 10. When you instantiate this class, the created object doesn't have cls_var attribute. When calling self.cls_var it is actually the class' attribute that is retrieved due to the way python resolves attributes.
However when set self.cls_var the value is set at the object level! So further call to self.cls_var will give you the value of the object's attribute, and not the class' anymore!
Maybe this bit of code will make this clearer:
class A(object):
a = 1
a = A()
print a.a # prints 1
A.a = 2
print a.a # prints 2
You see that even though when set the value at the class level, the changes are repercuted on the object, because, python will look up for the attribute in the class when it is not found at the object level.
When calling Test.cls_var it is the cls_var attribute of the class you are accessing! Not the one of the object you just modified.
So I have some god forsaken legacy code that uses the reserved word property, um wrong. In a base class that gets inherited they have basically implemented.
class TestClass(object):
def __init__(self, property):
self._property = property
#property
def property(self):
return self._property
test = TestClass('test property')
print(test.property)
Which runs without error. If you add another method below that you get,
class TestClass2(object):
def __init__(self, property):
self._property = property
#property
def property(self):
return self._property
#property
def other_property(self):
return 'test other property'
test = TestClass2('test property')
print(test.property)
print(test.other_property)
Which throws:
---> 10 #property
11 def other_property(self):
12 print('test other property')
TypeError: 'property' object is not callable
Because you know you have overwritten property in the local namespace.
class TestClass3(object):
def __init__(self, property):
self._property = property
#property
def other_property(self):
return 'test other property'
#property
def property(self):
return self._property
test = TestClass3('test property')
print(test.property)
print(test.other_property)
You can work around this if you always define your property overwrite at the bottom of your class. If the property method is only defined on the base class you inherit from things also work out, because namespaces.
class TestClass4(TestClass):
def __init__(self, property):
super(TestClass4, self).__init__(property)
#property
def other_property(self):
return 'test other property'
test = TestClass4('test property')
print(test.property)
print(test.other_property)
My righteous indignation says that we MUST update this variable name in the huge amount of legacy code, because GAAAAH, but other than having to remember to add new methods above the definition of property definition in the rarely modified base class, this doesn't actually break anything right?
dont shadow builtins... with almost no refactoring at all you can avoid shadowing the builtin entirely
use __getattr__ instead of #property to return your _property member ...
class TestClass(object):
def __init__(self):
self._property = 12
def __getattr__(self,item):
if item == "property":
#do your original getter code for `property` here ...
# now you have not overwritten the property keyword at all
return getattr(self,"_property") # just return the variable
class TestClass2(TestClass):
def __init__(self):
self._property = 67
print TestClass2().property
class MySubClass(TestClass):
#property
def a_property(self):
return 5
print MySubClass().property
print MySubClass().a_property
really, as an aside, theres not any good reason imho to use #property in python. all it does is end up confusing other programmers later and obscuring the fact that you are actually calling a function. I used to do it regularly ... I now avoid it unless I have a very very compelling reason not to
Yes, if you always add new methods above the definition of the property method nothing will break. So put a nice big comment to that effect in the code. Hopefully, anyone wanting to override property in a derived class will look at the base class first and see your comment...
BTW, the property arg to the __init__ method also shadows property, but I guess that's not an issue.
Ideally, someone should clean this mess up, but I understand that it may not be cost-effective to do that.
Also, I'm somewhat baffled why the original coder made property an #property in the first place. That construct should only be used when the value of the attribute has to be calculated dynamically, not for simply returning a static attribute. Perhaps they were new to Python and they were told that's the way to do getters in Python...
You could always remap property to another name. So long as you choose a name that doesn't match your other class attributes, and it won't be exposed in the external interface for the class, so it doesn't really matter what you name it.
tproperty = property
class Test(...)
#tproperty
def property(self):
....
In Python, I currently have instances of a class like MyClass('name1'), MyClass('name2') and so on.
I want to make it so that each instance has its own superclass, i.e., I want MyClass('name1') to be an instance of Name1MyClass and MyClass('name2') to be an instance of Name2MyClass. Name1MyClass and Name2MyClass would be dynamically generated subclasses of MyClass. I can't figure out how to do this, because it seems that Python always makes whatever is returned from __new__ an instance of that class. It isn't clear to me how to do it in a metaclass either.
The reason I want to do this is that I want to define __doc__ docstrings on the instances. But it seems that help completely ignores __doc__ on instances; it only looks on classes. So to put a different docstring on each instance, I need to make each instance have its own custom class.
I could be wrong, but I don't think you want a metaclass here. __metaclass__es are used when the class is created, not when you call the class to construct a new instance of the class (or something else).
Here's an answer using __new__ without a metaclass. It feels a bit hacky, but it seems to work:
_sentinel = Ellipsis
class MyClass(object):
def __new__(cls, name):
if name is _sentinel:
return object.__new__(cls)
else:
instance = type(name + cls.__name__, (MyClass,), {})(_sentinel)
# Initialization goes here.
return instance
print type(MyClass('name1'))
print type(MyClass('name2'))
There's a catch here -- All the business logic of initializing then new instance must be done in __new__. Since __new__ is returning a different type than the class it is bound to, __init__ won't get called.
Another option is to create a class factory:
class MyClass(object):
pass
def class_factory(name):
new_cls = type(name + MyClass.__name__, (MyClass,), {})
return new_cls() # Or pass whatever you want in here...
print type(class_factory('name1'))
print type(class_factory('name2'))
Finally, you could even create a non-__new__ class method:
class MyClass(object):
#classmethod
def class_factory(cls, name):
new_cls = type(name + cls.__name__, (cls,), {})
return new_cls() # Or pass whatever you want in here...
print type(MyClass.class_factory('name1'))
print type(MyClass.class_factory('name2'))