예제 #1
0
def main():
    hello = Hello(True, ['葛'])
    hello.inform(send_email)
    while True:
        hello.get_sport_rank()
        hello.add_friend()
        hello.get_friend()
        hello.save_to_mysql()
        time.sleep(3 * 60)
예제 #2
0
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['FLASK_APP_USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['FLASK_APP_PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in %s' % Hello().say())
            return redirect(url_for('show_entries'))
    return render_template('login.html', error=error)
예제 #3
0
def main():
    print("This is the test of test() in hello.py: ")
    hello.test()

    print("This is the test of main() in hello.py: ")
    hello.main()

    print("This is the test of class Hello in hello.py: ")
    tmp = hello.Hello()
    tmp.show_string()

    print("This is the test of class Hello via \"from hello import Hello\"")
    tmp = Hello()
    tmp.show_string()
예제 #4
0
def should_have_executed(monkeypatch) -> None:
    def mock_init(*args: tuple, **kwargs: dict) -> None:
        assert args[1] == ['on']

    execute_calls = []
    monkeypatch.setattr(f"{MODULE_NAME}.Bluetooth.__init__", mock_init)
    monkeypatch.setattr(f"{MODULE_NAME}.Bluetooth.execute",
                        lambda *a, **k: execute_calls.append(''))
    monkeypatch.setattr(f"{MODULE_NAME}.Wifi.__init__", mock_init)
    monkeypatch.setattr(f"{MODULE_NAME}.Wifi.execute",
                        lambda *a, **k: execute_calls.append(''))
    mute_logs(MODULE_NAME, monkeypatch)

    assert Hello().execute() == ''
    assert len(execute_calls) == 2
예제 #5
0
# -*- coding: utf-8 -*-

from hello import Hello

h = Hello()

h.hello('Shane')

print(type(Hello))

print(type(h))


def fn(self, name='World'):
    print('Hello, %s' % name)


Hello = type('Hello', (object, ), dict(hello2=fn))  #创建Hello class

h = Hello()

h.hello2()
예제 #6
0
def main():
    h = Hello()
    h.hello()
예제 #7
0
from hello import Hello
f=Hello()
f.hello()
예제 #8
0
from hello import Hello
from world import World

hello_world = Hello(World())
hello_world.say_something()

hello_world_second = Hello(World())
hello_world_second.say_something()
예제 #9
0
from hello import Hello
import sys
sys.path.append('G:\\eclipse mares programs\\Database Tests\\PE Resources Management\\UIs')#import from other directory
from PyQt5.QtWidgets import QApplication
from windowTesting import MyWindow
if __name__ == '__main__':
    app = QApplication(sys.argv) 
    dlg = Hello()
    dlg.show()
    sys.exit(app.exec_())
예제 #10
0
>>> def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

要创建一个class对象,type()函数依次传入3个参数:

class的名称;
继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

正常情况下,我们都用class Xxx...来定义类,但是,type()函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

#metaclass

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。

我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:

定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)


有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:

class MyList(list, metaclass=ListMetaclass):
    pass

当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

__new__()方法接收到的参数依次是:

当前准备创建的类的对象;

类的名字;

类继承的父类集合;

类的方法集合。

测试一下MyList是否可以调用add()方法:

>>> L = MyList()
>>> L.add(1)
>> L
[1]

而普通的list没有add()方法:

>>> L2 = list()
>>> L2.add(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
예제 #11
0
>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...     def __str__(self):
...         return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))
Student object (name: Michael)

这样打印出来的实例,不但好看,而且容易看出实例内部重要的数据。

但是细心的朋友会发现直接敲变量不用print,打印出来的实例还是不好看:

>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>

这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

#__iter__

如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration();
        return self.a # 返回下一个值

现在,试试把Fib实例作用于for循环:

>>> for n in Fib():
...     print(n)
...
1
1
2
3
5
...
46368
75025

#__getitem__

Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:

>>> Fib()[5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Fib' object does not support indexing

要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

现在,就可以按下标访问数列的任意一项了:

>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101

但是list有个神奇的切片方法:

>>> list(range(100))[5:10]
[5, 6, 7, 8, 9]

对于Fib却报错。原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L

现在试试Fib的切片:

>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

但是没有对step参数作处理:

>>> f[:10:2]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。

此外,如果把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,例如str。

与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

#__getattr__

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义Student类:

class Student(object):

    def __init__(self):
        self.name = 'Michael'

调用name属性,没问题,但是,调用不存在的score属性,就有问题了:

>>> s = Student()
>>> print(s.name)
Michael
>>> print(s.score)
Traceback (most recent call last):
  ...
AttributeError: 'Student' object has no attribute 'score'

错误信息很清楚地告诉我们,没有找到score这个attribute。

要避免这个错误,除了可以加上一个score属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99

当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99

返回函数也是完全可以的:

class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25

只是调用方式要变为:

>>> s.age()
25

注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。

此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。

举个例子:

现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:

http://api.server/user/friends
http://api.server/user/timeline/list

如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

利用完全动态的__getattr__,我们可以写出一个链式调用:

class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__

试试:

>>> Chain().status.user.timeline.list
'/status/user/timeline/list'

这样,无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变!

还有些REST API会把参数放到URL中,比如GitHub的API:

GET /users/:user/repos

调用时,需要把:user替换为实际用户名。如果我们能写出这样的链式调用:

Chain().users('michael').repos

就可以非常方便地调用API了。有兴趣的童鞋可以试试写出来。

#__call__

一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?在Python中,答案是肯定的。

任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。请看示例:

class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)

调用方式如下:

>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.
__call__()

还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例:

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False

通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

小结

Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类。

本节介绍的是最常用的几个定制方法,还有很多可定制的方法,请参考Python的官方文档。

参考源码

special_str.py

special_iter.py

special_getitem.py

special_getattr.py

special_call.py


##使用枚举类


当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份:

JAN = 1
FEB = 2
MAR = 3
...
NOV = 11
DEC = 12

好处是简单,缺点是类型是int,并且仍然是变量。

更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能:

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:

for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

value属性则是自动赋给成员的int常量,默认从1开始计数。

如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:

from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

@unique装饰器可以帮助我们检查保证没有重复值。

访问这些枚举类型可以有若干种方法:

>>> day1 = Weekday.Mon
>>> print(day1)
Weekday.Mon
>>> print(Weekday.Tue)
Weekday.Tue
>>> print(Weekday['Tue'])
Weekday.Tue
>>> print(Weekday.Tue.value)
2
>>> print(day1 == Weekday.Mon)
True
>>> print(day1 == Weekday.Tue)
False
>>> print(Weekday(1))
Weekday.Mon
>>> print(day1 == Weekday(1))
True
>>> Weekday(7)
Traceback (most recent call last):
  ...
ValueError: 7 is not a valid Weekday
>>> for name, member in Weekday.__members__.items():
...     print(name, '=>', member)
...
Sun => Weekday.Sun
Mon => Weekday.Mon
Tue => Weekday.Tue
Wed => Weekday.Wed
Thu => Weekday.Thu
Fri => Weekday.Fri
Sat => Weekday.Sat

可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。

小结

Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。

参考源码

use_enum.py


##使用元类


#type()

动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

比方说我们要定义一个Hello的class,就写一个hello.py模块:

class Hello(object):
    def hello(self, name='world'):
        print('Hello, %s.' % name)

当Python解释器载入hello模块时,就会依次执行该模块的所有语句,执行结果就是动态创建出一个Hello的class对象,测试如下:

>>> from hello import Hello
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class 'hello.Hello'>

type()函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello。

我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。

type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:

>>> def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

要创建一个class对象,type()函数依次传入3个参数:

class的名称;
继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

正常情况下,我们都用class Xxx...来定义类,但是,type()函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

#metaclass

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。

我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:

定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)


有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:

class MyList(list, metaclass=ListMetaclass):
    pass

当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

__new__()方法接收到的参数依次是:

当前准备创建的类的对象;

类的名字;

类继承的父类集合;

类的方法集合。

测试一下MyList是否可以调用add()方法:

>>> L = MyList()
>>> L.add(1)
>> L
[1]

而普通的list没有add()方法:

>>> L2 = list()
>>> L2.add(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'add'

动态修改有什么意义?直接在MyList定义中写上add()方法不是更简单吗?正常情况下,确实应该直接写,通过metaclass修改纯属变态。

但是,总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。

ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

让我们来尝试编写一个ORM框架。

编写底层模块的第一步,就是先把调用接口写出来。比如,使用者如果使用这个ORM框架,想定义一个User类来操作对应的数据库表User,我们期待他写出这样的代码:

class User(Model):
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

# 创建一个实例:
u = User(id=12345, name='Michael', email='*****@*****.**', password='******')
# 保存到数据库:
u.save()

其中,父类Model和属性类型StringField、IntegerField是由ORM框架提供的,剩下的魔术方法比如save()全部由metaclass自动完成。虽然metaclass的编写会比较复杂,但ORM的使用者用起来却异常简单。

现在,我们就按上面的接口来实现该ORM。

首先来定义Field类,它负责保存数据库表的字段名和字段类型:

class Field(object):

    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

在Field的基础上,进一步定义各种类型的Field,比如StringField,IntegerField等等:

class StringField(Field):

    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')

class IntegerField(Field):

    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

下一步,就是编写最复杂的ModelMetaclass了:

class ModelMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        attrs['__table__'] = name # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)

以及基类Model:

class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

当用户定义一个class User(Model)时,Python解释器首先在当前类User的定义中查找metaclass,如果没有找到,就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclass的ModelMetaclass来创建User类,也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。

在ModelMetaclass中,一共做了几件事情:

排除掉对Model类的修改;

在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个__mappings__的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);

把表名保存到__table__中,这里简化为表名默认为类名。

在Model类中,就可以定义各种操作数据库的方法,比如save(),delete(),find(),update等等。

我们实现了save()方法,把一个实例保存到数据库中。因为有表名,属性到字段的映射和属性值的集合,就可以构造出INSERT语句。

编写代码试试:

u = User(id=12345, name='Michael', email='*****@*****.**', password='******')
u.save()

输出如下:

Found model: User
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: id ==> <IntegerField:uid>
Found mapping: name ==> <StringField:username>
SQL: insert into User (password,email,username,id) values (?,?,?,?)
ARGS: ['my-pwd', '*****@*****.**', 'Michael', 12345]

可以看到,save()方法已经打印出了可执行的SQL语句,以及参数列表,只需要真正连接到数据库,执行该SQL语句,就可以完成真正的功能。

不到100行代码,我们就通过metaclass实现了一个精简的ORM框架。

小结

metaclass是Python中非常具有魔术性的对象,它可以改变类创建时的行为。这种强大的功能使用起来务必小心。

参考源码

create_class_on_the_fly.py

use_metaclass.py

orm.py
예제 #12
0
 def setUp(self):
     self.x = Hello(88)
예제 #13
0
 def setUp(self):
     self.hello = Hello()
예제 #14
0
파일: day19.py 프로젝트: 1399959932/python
	Sat => Weekday.Sat

使用元类
	type()函数可以查看一个类型或变量的类型
	动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的
	class Hello(object):
    def hello(self, name='world'):
        print('Hello, %s.' % name)

    >>> from hello import Hello #我失败了, 建了hello.py文件但是
	 	# from hello import Hello
		# Traceback (most recent call last):
		#   File "<pyshell#132>", line 1, in <module>
		#     from hello import Hello
		# ModuleNotFoundError: No module named 'hello'
	>>> h = Hello()
	>>> h.hello()
	Hello, world.
	>>> print(type(Hello))	#Hello是一个class,它的类型就是type
	<class 'type'>
	>>> print(type(h))	#而h是一个实例,它的类型就是class Hello
	<class 'hello.Hello'>


	我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数
	type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:
	
	>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
	>>> h = Hello()
	>>> h.hello()
	Hello, world.
예제 #15
0
 def test_hello(self):
     hello = Hello()
     self.assertEqual(hello.output("Santa Claus"), "Hello, Santa Claus!")
예제 #16
0
>>> from Hello import Hello
Traceback (most recent call last):
  File "<pyshell#130>", line 1, in <module>
    from Hello import Hello
ModuleNotFoundError: No module named 'Hello'
>>> from hello import Hello
Traceback (most recent call last):
  File "<pyshell#131>", line 1, in <module>
    from hello import Hello
ModuleNotFoundError: No module named 'hello'
>>> from hello import Hello
Traceback (most recent call last):
  File "<pyshell#132>", line 1, in <module>
    from hello import Hello
ModuleNotFoundError: No module named 'hello'
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
>>> def fn(self, name='world'):
	print('Hello, %s.' % name)

	
>>> def fn(self, name='world'):
	print('Hello, %s.' % name)

	
>>> Hello = type('Hello', (object,), dict(hello=fn)))
예제 #17
0
from hello import Hello

var = 10
print(var)
print(Hello().__var)
예제 #18
0
from hello import Hello
#当解释器载入hello模块时,就会意思执行该模块的代码,执行结果就是动态创建出一个Hello的class对象

h = Hello()
h.hello()
print(type(Hello))
print(type(h))


#type()函数既可以返回一个对象的类型,又可以创建出新的类型。
#eg 用type()函数创建出Hello类。
def fn(self, name='world'):  #先定义函数
    print(f'Hello {name}')


Hello = type('Hello', (), dict(hello=fn))  #创建Hello class

h1 = Hello()
h1.hello()
'''
要创建一个class对象,type()函数依次传入3个参数:

1.class的名称;
2.继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
3.class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上
通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
'''

#metaclass 元类
'''
可以使用metaclass 控制类的创建行为
예제 #19
0
from doctor import Doctor
from hall_of_fame import HallOfFame

bot = Bot(command_prefix='$')


@bot.listen()
async def on_ready():
    for guild in bot.guilds:
        print(f'Guild: {guild.name}, {guild.id}')


if __name__ == '__main__':
    bot.remove_command('help')
    bot.add_cog(Help(bot))
    bot.add_cog(Hello(bot))
    bot.add_cog(Covid(bot))
    bot.add_cog(X(bot))
    bot.add_cog(Dice(bot))
    bot.add_cog(Landmine(bot))
    bot.add_cog(Sleep(bot))
    bot.add_cog(Tex(bot))
    bot.add_cog(Mine(bot))
    bot.add_cog((MessagePlayback(bot)))
    bot.add_cog(Doctor(bot))
    bot.add_cog(HallOfFame(bot))
    # bot.add_cog(Cropped(bot))

    TOKEN = os.environ['DISCORD_BOT_TOKEN']
    # sleepCommand = sleep.Sleep()
    bot.run(TOKEN)
예제 #20
0
 def test_env(self):
     self.assertEqual(Hello().hello(), "hello python")
예제 #21
0
파일: main.py 프로젝트: gp888/PythonTest
元类

动态语言
函数和类的定义,不是编译时定义的,而是运行时动态创建的

hello.py模块:
class Hello(object):
    def hello(self, name='world'):
        print('Hello, %s.' % name)

当Python解释器载入hello模块时
会执行该模块的所有语句
动态创建出一个Hello的class对象

from hello import Hello
h = Hello()
h.hello()
Hello, world.
print(type(Hello)) #Hello是一个class,它的类型就是type
<class 'type'>
print(type(h))
<class 'hello.Hello'>

class的定义是运行时动态创建的
创建class的方法就是使用type()函数

type()函数既可以返回一个对象的类型,又可以创建出新的类型

可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义

def fn(self, name='world'): # 先定义函数
예제 #22
0
# A simple Hello World web application

from flask import Flask, request, redirect, url_for, render_template
from hello import Hello

app = Flask(__name__)
hello = Hello("Web")


@app.route("/", methods=["GET", "POST"])
def helloweb():
    """Display a greeting."""

    if request.method == "POST":
        # We have received some form data

        if not request.form["name"]:
            # We didn't get a name, pick a random one
            return redirect(url_for('random'))
        else:
            # We got a name, update our Hello object
            hello.name = request.form["name"]
            hello.add(request.form["name"])

    # Render the page
    return render_template("helloweb.html", hello=str(hello))


@app.route("/random")
def random():
    """Pick a random name and display the greeting."""
예제 #23
0
def should_have_printed_usage_instructions(monkeypatch) -> None:
    print_coloured_calls = []
    monkeypatch.setattr(f"{MODULE_NAME}.print_coloured",
                        lambda *a, **k: print_coloured_calls.append(''))
    Hello().usage()
    assert len(print_coloured_calls) == 2
예제 #24
0
def reset():
    """Reset the internal state."""

    global hello
    hello = Hello("Web")
    return redirect(url_for('helloweb'))
예제 #25
0
from sys import exit
from tkinter import *  # get Tk widget classes
from hello import Hello  # get the subframe class

parent = Frame(None)  # make a container widget
parent.pack()
Hello(parent).pack(side=RIGHT)  # attach Hello instead of running it

Button(parent, text='Attach', command=exit).pack(side=LEFT)
parent.mainloop()
 def __init__(self):
     self._hello = Hello()
     self._result = ''
예제 #27
0
 def test_hey(self):
     h = Hello()
     res = h.hey()
     print(res)
     assert res == 'mock'
예제 #28
0
from hello import Hello

hello = Hello()
hello.echo()
예제 #29
0
from hello import Hello
h = Hello()
h.hello()
print(type(Hello))
print(type(h))
예제 #30
0
        模块名即命名空间,调用该模块中的方法:模块名.方法()
        
        from 模块名 import 函数名:
            该语句将模块中的函数引入进来,我们就可以直接调用该方法名来执行函数,不需要加上命名空间了
            
        import 模块名 as 新名字:
            给命名空间替换一个新的名字,可以减少命名空间的代码量

'''
#3种导入模块的方法

import hello
hello.Hello()

from hello import Hello
Hello()

import hello as h
h.Hello()
print("=====================")

'''
    二.__name__ == '__main__'
        如果该模块内部调用了自己方法,当我们导入的时候,其内部调用的方法就会跟着执行,但这并不是我们想要的,
        所以我们需要让python知道,该模块是作为程序被调用还是导入到了其他的程序中
        
        此时我们便需要调用模块的__name__属性:
            当一个程序运行时,该程序的__name__属性的值是'__main__',
            而再导入模块的时候,这个值就是该模块的名字
'''
import test