• Archive by category "python"

Blog Archives

zerorpc通用Client实例直接调用特定server RPC方法原理

假设server端代码定义了一个RPC方法hello

import zerorpc
class HelloRPC(object):
    def hello(self, name):
        return "Hello, %s" % name

s = zerorpc.Server(HelloRPC())
s.bind("tcp://0.0.0.0:4242")
s.run()

在客户端代码如果要调用只需简单新建一个Client实例,然后直接以client.hello()调用。

import zerorpc
client = zerorpc.Client()
client.connect("tcp://127.0.0.1:4242")
print client.hello("RPC")

Client作为一个通用类,并未定义def hello(name):方法,直接以client.hello()调用是怎么做到的?答案就是利用了python的__getattr____call__,关键代码如下。

class Client(object):
    def __init__(self, ):
        pass

    def __call__(self, method, *args, **kwargs):
        # 在这连接server,序列化参数,请求,反序列化响应数据,并return
        pass

    def __getattr__(self, method):
        # 返回一个闭包,该闭包封装了self和method.
        # 当这个闭包被执行时,即x(*args, **kwargs),
        # 即self(method, *args, **kargs)被运行,
        # 即self.__call__(self, method, *args, **kwargs)
        f = lambda *args, **kwargs: self(method, *args, **kwargs)
        return f

当执行client.hello('RPC')时,会分两步,先获取hello这个属性的值,然后调用这个值。即先调用client.__getattr__('hello')查询hello这个属性的值,得到f,而f是一个闭包函数,然后调用f('RPC')

python MySQLdb例子

以下代码使用了torndb(torndb是对mysqldb的简单封装,调用和使用cursor一致)
mysqldb如果查询时传入的参数是值序列,则sql的paramstyle必须是format,即%s作为占位符;
如果传入的参数是键值序列,则sql的paramstyle必须是pyformat,即%(xxx)s作为占位符;

1.count

    def get_all_topics_count_by_node_slug(self, node_slug):
        sql = 'SELECT COUNT(0) FROM topic LEFT JOIN node ON topic.node_id = node.id WHERE node.slug = %s'
        return self.db.get(sql, node_slug)['COUNT(0)']

2.select(分页)

    def get_all_topics_by_node_slug(self, node_slug, current_page=1, page_size=36, ):
        sql = '''SELECT topic.*,
                author_user.username as author_username,
                author_user.nickname as author_nickname,
                author_user.avatar as author_avatar,
                author_user.uid as author_uid,
                author_user.reputation as author_reputation,
                node.name as node_name,
                node.slug as node_slug,
                last_replied_user.username as last_replied_username,
                last_replied_user.nickname as last_replied_nickname
                FROM topic
                LEFT JOIN user AS author_user ON topic.author_id = author_user.uid
                LEFT JOIN node ON topic.node_id = node.id
                LEFT JOIN user AS last_replied_user ON topic.last_replied_by = last_replied_user.uid
                WHERE node.slug = %s
                ORDER BY last_touched DESC, created DESC, last_replied_time DESC, id DESC
                LIMIT %s, %s'''

        total_count = self.get_all_topics_count_by_node_slug(node_slug)
        total_page = int(math.ceil(total_count / float(page_size)))
        current_page = current_page if current_page <= total_page else total_page
        current_page = current_page if current_page >= 1 else 1
        previous_page = current_page - 1 if current_page > 1 else 1
        next_page = current_page + 1 if current_page < total_page else total_page

        result = {
            "list": self.db.query(sql, node_slug, (current_page-1)*page_size, page_size),
            "page": {
                "prev": previous_page,
                "next": next_page,
                "current": current_page,
                "pages": total_page,
                "total": total_count,
                "size": page_size
            }
        }
        return result

3.insert(任意字段)

    def add_new_topic(self, topic_info):
        sql = 'INSERT INTO topic ('
        sql += ', '.join(topic_info.keys())
        sql += ') VALUES ('
        sql += ', '.join(['%s'] * len(topic_info.keys()))
        sql += ')'
        return self.db.insert(sql, *topic_info.itervalues())

4.update(任意字段,pyformat)

    def update_topic_by_topic_id(self, topic_id, topic_info):
        sql = 'UPDATE topic SET '
        sql += ', '.join(['%s = %%(%s)s' % (k, k) for k in topic_info.keys()])
        sql += ' WHERE id = %(id)s'
        print sql
        return self.db.update(sql, id=topic_id, **topic_info)

python db api正确使用方式

很多人在用Python DB API时会用以下方式拼接sql

cmd = "update people set name='%s' where id='%s'" % (name, id) curs.execute(cmd)

然后调用cursor.execute执行,这样容易产生sql注入

正确的方法是使用占位符语法

cmd = "update people set name=%s where id=%s" 
curs.execute(cmd, (name, id))

execute语句在执行时会先对元组(name, id)的值转为字符串,进行转义,

不同的数据库支持不同占位符语法,常见占位符包括以下:
1.’qmark’ Question mark style, e.g. ‘…WHERE name=?’
2.’numeric’ Numeric, positional style, e.g. ‘…WHERE name=:1’ ‘named’
3.’named’ Named style, e.g. ‘…WHERE name=:name’
4.’format’ ANSI C printf format codes, e.g. ‘…WHERE name=%s’
5.’pyformat’ Python extended format codes, e.g. ‘…WHERE name=%(name)s’
注意pyformat后的s

常见数据库python链接库实现的默认paramstyle

>>>import MySQLdb; 
print MySQLdb.paramstyle 
format 
>>> import psycopg2; 
>>>print psycopg2.paramstyle 
pyformat
>>> import sqlite3; 
print sqlite3.paramstyle 
qmark

如果你在使用MySQL 或 PostgreSQL, 可以用%s(即使是数字或者非字符)

更全的关于python防止sql注入的资料下载sqlinjection-120205200846-phpapp01

检查安装的Python是否支持UCS-4

>>> import sys
>>> print sys.maxunicode

当用--enable-unicode=ucs4编译安装时,输出的是1114111;
当用--enable-unicode=ucs2编译安装时,输出的是65535.