day3 - 编写ORM
2015-06-03 16:40
344 查看
···@classmethod
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法,而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢?
从它们的使用上来看,
@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样;
@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
而@classmethod因为持有cls参数,可以来调用 类的属性,类的方法,实例化对象 等,避免硬编码。cls全权代理类名!
例:
class A(object): bar = 1 def foo(self): print 'foo' @staticmethod def static_foo(): print 'static_foo' print A.bar @classmethod def class_foo(cls): print 'class_foo' print cls.bar cls().foo() #相当于 A().foo() A.static_foo() A.class_foo()
输出
static_foo
1
class_foo
1
foo
···dict的get()方法
语法:get( key [, default] )Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.
···补充:在父类中登记所有子类的信息(如类名等) S.O.
#-*-coding:utf-8-*- class Meta(type): def __new__(cls, clsname, bases, dct): res = type.__new__(cls, clsname, bases, dct) print 'res:%s\ncls:%s\nclsname:%s\nbases:%s\ndct:%s' %(res,cls,clsname,bases,dct) for cls in bases: #判断bases中的类(clsname的父类)是否MetaClass创建 #Base是object的派生类,但object不是由MetaClass创建 #Extend1,Extend2继承自Base,而Base是由MetaClass创建 if isinstance(cls, Meta): print 'cls:%s' %cls try: cls.extending(res) #res就是 Base 中 extending的subclass参数,相当于执行 Base.extending(res) except AttributeError: pass print 'cls:%s\nres:%s\n' %(cls,res) return res class Base(object): __metaclass__ = Meta subclasses = {} @classmethod def extending(cls, subclass): cls.subclasses[subclass.__name__] = subclass #相当于执行Base.subclasses[subclass.__name__] = subclass class Extend1(Base): pass class Extend2(Base): pass print Base.subclasses结果:
···dict类型的数据作为**kw关键字参数
>>> kw = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **kw) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
由此可见,dict 类型的数据是可以直接作为**kw参数传递给函数的(因为**kw本身就是 dict 类型的数据)。
当然,这样子也是可以的(用 “ = ”)
>>> person('Bob', 35, city='Beijing') name: Bob age: 35 other: {'city': 'Beijing'} >>> person('Adam', 45, gender='M', job='Engineer') name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
···完整代码剖析
注意一点,cls是class的简写。cls的含义在不同地方的含义是不一样的:在ModelMetaclass中出现的cls指代的 是也只能是 ModelMetaclass(对象,object——当然可以有自己的属性subclasses)——这是的cls用mcs代替就更直观了;而在Model中出现的cls指代的是Model或其他以Model为父类的派生类,在测定例中是User,当然还可以是School,Book等等,只要继承自Model就可以。以 defget(cls, pk): 为例,测试中调用User.get(10190),cls就是User,相当于实例调用中的self,只不过用了@classmethod之后,可以用类直接调用而已。
# -*- coding: utf-8 -*- ''' Database operation module. This module is independent with web module. ''' import time, logging import db class Field(object): _count = 0 #Field的类属性 def __init__(self, **kw): self.name = kw.get('name', None) self._default = kw.get('default', None) self.primary_key = kw.get('primary_key', False) #primary_key只能有一个 self.nullable = kw.get('nullable', False) #默认不可空 self.updatable = kw.get('updatable', True) self.insertable = kw.get('insertable', True) #表的每一列都是默认可插入的 self.ddl = kw.get('ddl', '') self._order = Field._count #记录参数(id,name,email,passwd,last_modified)的序号 Field._count = Field._count + 1 @property #@property 把方法变成属性 def default(self): d = self._default return d() if callable(d) else d def __str__(self): s = ['<%s:%s,%s,default(%s),' % (self.__class__.__name__, self.name, self.ddl, self._default)] self.nullable and s.append('N') self.updatable and s.append('U') self.insertable and s.append('I') s.append('>') return ''.join(s) class StringField(Field): def __init__(self, **kw): if not 'default' in kw: kw['default'] = '' if not 'ddl' in kw: kw['ddl'] = 'varchar(255)' super(StringField, self).__init__(**kw) class IntegerField(Field): def __init__(self, **kw): if not 'default' in kw: kw['default'] = 0 if not 'ddl' in kw: kw['ddl'] = 'bigint' super(IntegerField, self).__init__(**kw) class FloatField(Field): def __init__(self, **kw): if not 'default' in kw: kw['default'] = 0.0 if not 'ddl' in kw: kw['ddl'] = 'real' super(FloatField, self).__init__(**kw) class BooleanField(Field): def __init__(self, **kw): if not 'default' in kw: kw['default'] = False if not 'ddl' in kw: kw['ddl'] = 'bool' super(BooleanField, self).__init__(**kw) class TextField(Field): def __init__(self, **kw): if not 'default' in kw: kw['default'] = '' if not 'ddl' in kw: kw['ddl'] = 'text' super(TextField, self).__init__(**kw) class BlobField(Field): def __init__(self, **kw): if not 'default' in kw: kw['default'] = '' if not 'ddl' in kw: kw['ddl'] = 'blob' super(BlobField, self).__init__(**kw) class VersionField(Field): def __init__(self, name=None): super(VersionField, self).__init__(name=name, default=0, ddl='bigint') _triggers = frozenset(['pre_insert', 'pre_update', 'pre_delete']) #attrs['__sql__'] = lambda self: _gen_sql(attrs['__table__'], mappings) def _gen_sql(table_name, mappings): pk = None sql = ['-- generating SQL for %s:' % table_name, 'create table `%s` (' % table_name] #sql是字符串元素组成的list for v in sorted(mappings.values(), lambda x, y: cmp(x._order, y._order)): #mappings.values()是个list,元素是实例(object) if not hasattr(v, 'ddl'): raise StandardError('no ddl in field "%s".' % n) ddl = v.ddl nullable = v.nullable if v.primary_key: pk = v.name sql.append(nullable and ' `%s` %s,' % (v.name, ddl) or ' `%s` %s not null,' % (v.name, ddl)) sql.append(' primary key(`%s`)' % pk) sql.append(');') return '\n'.join(sql) class ModelMetaclass(type): ''' Metaclass for model objects. ''' def __new__(cls, name, bases, attrs): # skip base Model class: if name=='Model': return type.__new__(cls, name, bases, attrs) ''' store all subclasses info: 记录所有由ModelMetaclass所建的子类的信息 如果cls即ModelMetaclass类(只有类可以设置属性)没有subclasses属性, 就创建一个,是字典类型的数据,存储调用ModelMetaclass而创建的类的类名(如User,School等类) ''' if not hasattr(cls, 'subclasses'): cls.subclasses = {} if not name in cls.subclasses: cls.subclasses[name] = name else: logging.warning('Redefine class: %s' % name) logging.info('Scan ORMapping %s...' % name) mappings = dict() primary_key = None for k, v in attrs.iteritems(): if isinstance(v, Field): if not v.name: #v.name为空字符串,就默认用attrs的key值做name,id = IntegerField(primary_key=True) v.name = k logging.info('Found mapping: %s => %s' % (k, v)) # check duplicate primary key: if v.primary_key: if primary_key: raise TypeError('Cannot define more than 1 primary key in class: %s' % name) if v.updatable: logging.warning('NOTE: change primary key to non-updatable.') v.updatable = False if v.nullable: #保证pk默认不为空 logging.warning('NOTE: change primary key to non-nullable.') v.nullable = False primary_key = v #名花有主 mappings[k] = v # check exist of primary key: if not primary_key: raise TypeError('Primary key not defined in class: %s' % name) for k in mappings.iterkeys(): attrs.pop(k) if not '__table__' in attrs: #默认在用户接口不需要写__table__类属性(则默认等于类名的小写字母) attrs['__table__'] = name.lower() attrs['__mappings__'] = mappings attrs['__primary_key__'] = primary_key #primary_key作为类属性(也是个类,有属性) attrs['__sql__'] = lambda self: _gen_sql(attrs['__table__'], mappings) for trigger in _triggers: #继续添加属性 if not trigger in attrs: attrs[trigger] = None return type.__new__(cls, name, bases, attrs) class Model(dict): ''' Base class for ORM. >>> class User(Model): ... id = IntegerField(primary_key=True) ... name = StringField() ... email = StringField(updatable=False) ... passwd = StringField(default=lambda: '******') ... last_modified = FloatField() ... def pre_insert(self): ... self.last_modified = time.time() >>> u = User(id=10190, name='Michael', email='orm@db.org') >>> r = u.insert() >>> u.email 'orm@db.org' >>> u.passwd '******' >>> u.last_modified > (time.time() - 2) True >>> f = User.get(10190) #因为用了@classmethod,所以可以直接使用类名来调用方法(也必须用类名来调用,这意味着必须用@classmethod来写类的方法) >>> f.name u'Michael' >>> f.email u'orm@db.org' >>> f.email = 'changed@db.org' >>> r = f.update() # change email but email is non-updatable! >>> len(User.find_all()) 1 >>> g = User.get(10190) >>> g.email u'orm@db.org' >>> r = g.delete() >>> len(db.select('select * from user where id=10190')) 0 >>> import json >>> print User().__sql__() -- generating SQL for user: create table `user` ( `id` bigint not null, `name` varchar(255) not null, `email` varchar(255) not null, `passwd` varchar(255) not null, `last_modified` real not null, primary key(`id`) ); ''' __metaclass__ = ModelMetaclass def __init__(self, **kw): super(Model, self).__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'Dict' object has no attribute '%s'" % key) def __setattr__(self, key, value): self[key] = value @classmethod def get(cls, pk): #cls参数无需传入,cls就是User,调用就是User.get(pk) ''' Get by primary key. ''' d = db.select_one('select * from %s where %s=?' % (cls.__table__, cls.__primary_key__.name), pk) return cls(**d) if d else None #cls(**d)等价于User(**d),d 是个dict @classmethod def find_first(cls, where, *args): #where形如 'id=?' , 调用如:User.find_first(id=?, 100) ''' Find by where clause and return one result. If multiple results found, only the first one returned. If no result found, return None. ''' #d = db.select_one('select * from ? ?' % (cls.__table__, where), *args)也是可以的 d = db.select_one('select * from %s %s' % (cls.__table__, where), *args) return cls(**d) if d else None #cls(**d)等价于User(**d),d 是个dict @classmethod def find_all(cls, *args): ''' Find all and return list. ''' L = db.select('select * from `%s`' % cls.__table__) return [cls(**d) for d in L] #L是list,d是dict(L的元素),cls(**d)等价于User(**d) @classmethod def find_by(cls, where, *args): ''' Find by where clause and return list. ''' L = db.select('select * from `%s` %s' % (cls.__table__, where), *args) return [cls(**d) for d in L] @classmethod def count_all(cls): ''' Find by 'select count(pk) from table' and return integer. ''' return db.select_int('select count(`%s`) from `%s`' % (cls.__primary_key__.name, cls.__table__)) @classmethod def count_by(cls, where, *args): ''' Find by 'select count(pk) from table where ... ' and return int. ''' return db.select_int('select count(`%s`) from `%s` %s' % (cls.__primary_key__.name, cls.__table__, where), *args) def update(self): self.pre_update and self.pre_update() L = [] args = [] for k, v in self.__mappings__.iteritems(): if v.updatable: if hasattr(self, k): arg = getattr(self, k) else: arg = v.default setattr(self, k, arg) L.append('`%s`=?' % k) args.append(arg) pk = self.__primary_key__.name #实例没有__primary_key__属性,就使用类的:User.__primary_key__ args.append(getattr(self, pk)) db.update('update `%s` set %s where %s=?' % (self.__table__, ','.join(L), pk), *args) return self def delete(self): self.pre_delete and self.pre_delete() pk = self.__primary_key__.name args = (getattr(self, pk), ) db.update('delete from `%s` where `%s`=?' % (self.__table__, pk), *args) return self ''' @property #@property 把方法变成属性 def default(self): d = self._default return d() if callable(d) else d ''' def insert(self): self.pre_insert and self.pre_insert() #如果有self.pre_insert(就是 u.pre_insert ) 就执行( and 是‘与’ )! params = {} for k, v in self.__mappings__.iteritems(): if v.insertable: if not hasattr(self, k): setattr(self, k, v.default) #default本来是类的方法,用了@property后可以直接用属性的方法调用。这里相当于self[k]=v.default params[v.name] = getattr(self, k) #等价于params[v.name] = self[k] db.insert('%s' % self.__table__, **params) return self if __name__=='__main__': logging.basicConfig(level=logging.DEBUG) db.create_engine('root', '19921005', 'test') db.update('drop table if exists user') db.update('create table user (id int primary key, name text, email text, passwd text, last_modified real)') import doctest doctest.testmod()
相关文章推荐
- STL系列之八 slist单链表
- Android android:gravity与 android:layout_gravity的区别
- .NET项目持续集成实践 - Jenkins
- error C2018: unknown character '0xa1'
- UVA 10600 ACM Contest and Blackout (次小生成树)
- 集合如何判断null
- SQL在线优化工具(支持java String 和String Bufer)
- Excel导入导出(二)导入模版定制
- (转) git 常用命令整理
- 更改Map过程中用于区分不同key/value对的分隔符
- 第十三周 项目二 形状类族中的纯虚函数
- UIButton使用汇总
- poj 3320 尺取法
- java double比较大小
- 通过 KVM+virt-manager配置双屏虚拟机(两套键盘。鼠标)
- HDU 1875 畅通工程再续 (最小生成树)
- 苹果应用商店审核指南中文翻译
- Java for LeetCode 138 Copy List with Random Pointer
- 字符串如何判断null.
- hdoj2151简单的计数DP