2

Suppose we have the following code:

class A:
    var = 0
a = A()

I do understand that a.var and A.var are different variables, and I think I understand why this thing happens. I thought it was just a side effect of python's data model, since why would someone want to modify a class variable in an instance?

However, today I came across a strange example of such a usage: it is in google app engine db.Model reference. Google app engine datastore assumes we inherit db.Model class and introduce keys as class variables:

class Story(db.Model):
    title = db.StringProperty()
    body = db.TextProperty()
    created = db.DateTimeProperty(auto_now_add=True)
s = Story(title="The Three Little Pigs")

I don't understand why do they expect me to do like that? Why not introduce a constructor and use only instance variables?

karlicoss
  • 2,501
  • 4
  • 25
  • 29
  • as long as internally it refers to `self.var` and not to `CLASSNAME.var` then it is only modifying the value for its own instance effectively becomming an instance variable... – Joran Beasley Jul 10 '12 at 22:04
  • @JoranBeasley yep, they do behave like instance variable, so why don't make them instance? :) Class variables look there excessive. – karlicoss Jul 10 '12 at 22:09
  • note appengine uses metaclasses. The property definition in your class is creating instances of Property objects that manage the mapping of data sourced from the datastore to an instantiated object. This is the same approach used by other ORM based libs like SQLAlchemy, Storm etc.. Just go with the flow and if you want to know more read up on metaclasses and metaprogramming. – Tim Hoffman Jul 11 '12 at 01:19

6 Answers6

3

The db.Model class is a 'Model' style class in classic Model View Controller design pattern. Each of the assignments in there are actually setting up columns in the database, while also giving an easy to use interface for you to program with. This is why

title="The Three Little Pigs"

will update the object as well as the column in the database.

There is a constructor (no doubt in db.Model) that handles this pass-off logic, and it will take a keyword args list and digest it to create this relational model.

This is why the variables are setup the way they are, so that relation is maintained.

Edit: Let me describe that better. A normal class just sets up the blue print for an object. It has instance variables and class variables. Because of the inheritence to db.Model, this is actually doing a third thing: Setting up column definitions in a database. In order to do this third task it is making EXTENSIVE behinds the scenes changes to things like attribute setting and getting. Pretty much once you inherit from db.Model you aren't really a class anymore, but a DB template. Long story short, this is a VERY specific edge case of the use of a class

Jeremy
  • 81
  • 4
  • And if title was instance variable would it be impossible to do that? – karlicoss Jul 10 '12 at 22:15
  • 1
    Edit: No, let me describe that better. A normal class just sets up the blue print for an object. It has instance variables and class variables. Because of the inheritence to db.Model, this is actually doing a third thing: Setting up column definitions in a database. In order to do this third task it is making EXTENSIVE behinds the scenes changes to things like attribute setting and getting. Pretty much once you inherit from db.Model you aren't really a class anymore, but a DB template. Long story short, this is a VERY specific edge case of the use of a class. – Jeremy Jul 10 '12 at 22:30
  • So, you mean the same as Luke? Thanks, than I finally got it :) I'll mark your answer as correct, could you merge your comment with the answer body please? – karlicoss Jul 10 '12 at 22:40
  • If you're curious, you should read up on python metaclasses - classes used to create other classes, which is used behind the scenes in db.Model. http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python – dragonx Jul 11 '12 at 01:21
1

If all variables are declared as instance variables then the classes using Story class as superclass will inherit nothing from it.

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
1

From the Model and Property docs, it looks like Model has overridden __getattr__ and __setattr__ methods so that, in effect, "Story.title = ..." does not actually set the instance attribute; instead it sets the value stored with the instance's Property.

If you ask for story.__dict__['title'], what does it give you?

Luke
  • 11,374
  • 2
  • 48
  • 61
  • Oh, I think i get you. You mean it's done to directly access the value wrapped into a corresponding Property instead of doing something like story.title = db.StringProperty(title)? – karlicoss Jul 10 '12 at 22:20
  • That's my suspicion, anyway. I think I'd have to peek at the source code to be sure; the documentation wasn't very clear about the implementation. – Luke Jul 12 '12 at 01:58
  • No, there's no need to use `__getattr__` and `__setattr__`; model properties are descriptors. – Nick Johnson Jul 27 '12 at 07:35
1

I do understand that a.var and A.var are different variables

First off: as of now, no, they aren't.

In Python, everything you declare inside the class block belongs to the class. You can look up attributes of the class via the instance, if the instance doesn't already have something with that name. When you assign to an attribute of an instance, the instance now has that attribute, regardless of whether it had one before. (__init__, in this regard, is just another function; it's called automatically by Python's machinery, but it simply adds attributes to an object, it doesn't magically specify some kind of template for the contents of all instances of the class - there's the magic __slots__ class attribute for that, but it still doesn't do quite what you might expect.)

But right now, a has no .var of its own, so a.var refers to A.var. And you can modify a class attribute via an instance - but note modify, not replace. This requires, of course, that the original value of the attribute is something modifiable - a list qualifies, a str doesn't.

Your GAE example, though, is something totally different. The class Story has attributes which specifically are "properties", which can do assorted magic when you "assign to" them. This works by using the class' __getattr__, __setattr__ etc. methods to change the behaviour of the assignment syntax.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
1

The other answers have it mostly right, but miss one critical thing.

If you define a class like this:

class Foo(object):
  a = 5

and an instance:

myinstance = Foo()

Then Foo.a and myinstance.a are the very same variable. Changing one will change the other, and if you create multiple instances of Foo, the .a property on each will be the same variable. This is because of the way Python resolves attribute access: First it looks in the object's dict, and if it doesn't find it there, it looks in the class's dict, and so forth.

That also helps explain why assignments don't work the way you'd expect given the shared nature of the variable:

>>> bar = Foo()
>>> baz = Foo()
>>> Foo.a = 6
>>> bar.a = 7
>>> bar.a
7
>>> baz.a
6

What happened here is that when we assigned to Foo.a, it modified the variable that all instance of Foo normally resolve when you ask for instance.a. But when we assigned to bar.a, Python created a new variable on that instance called a, which now masks the class variable - from now on, that particular instance will always see its own local value.

If you wanted each instance of your class to have a separate variable initialized to 5, the normal way to do it would be like this:

class Foo(object);
  def __init__(self):
    self.a = 5

That is, you define a class with a constructor that sets the a variable on the new instance to 5.

Finally, what App Engine is doing is an entirely different kind of black magic called descriptors. In short, Python allows objects to define special __get__ and __set__ methods. When an instance of a class that defines these special methods is attached to a class, and you create an instance of that class, attempts to access the attribute will, instead of setting or returning the instance or class variable, they call the special __get__ and __set__ methods. A much more comprehensive introduction to descriptors can be found here, but here's a simple demo:

class MultiplyDescriptor(object):
  def __init__(self, multiplicand, initial=0):
    self.multiplicand = multiplicand
    self.value = initial

  def __get__(self, obj, objtype):
    if obj is None:
      return self
    return self.multiplicand * self.value

  def __set__(self, obj, value):
    self.value = value

Now you can do something like this:

class Foo(object):
  a = MultiplyDescriptor(2)

bar = Foo()
bar.a = 10
print bar.a # Prints 20!

Descriptors are the secret sauce behind a surprising amount of the Python language. For instance, property is implemented using descriptors, as are methods, static and class methods, and a bunch of other stuff.

Nick Johnson
  • 100,655
  • 16
  • 128
  • 198
0

These class variables are metadata to Google App Engine generate their models.

FYI, in your example, a.var == A.var.

>>> class A:
...     var = 0
... 
... a = A()
... A.var = 3
... a.var == A.var
1: True
iurisilvio
  • 4,868
  • 1
  • 30
  • 36