hammer崔的程序世界

我的生涯一片无悔,我想起那个午夜在灯泡下的抠代码,那是我逝去的青春!


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索
close

数据库进阶:1 win10环境mongoDB部署

发表于 2016-09-13   |   分类于 数据库   |  

win10环境部署

下载

下载地址官网
选择如图:

安装过程,选择>>custom,这个是免费版本

环境变量

  1. 把安装之后的F:\App\mongoDb3.2.9\bin添加到环境变量Path中
  2. f盘根目录新建mongodb\data目录,’mongodb\logs\db.log`文件

命令行启动数据库

  1. 管理员启动cmd,输入mongod.exe --dbpath=f:\mongodb\data
    显示
    说明启动成功
  2. 浏览器输入http://localhost:27017/,会出现

    1
    It looks like you are trying to access MongoDB over HTTP on the native driver port.
  3. lunix环境将mongodb作为windows服务随机启动
    cmd命令输入

    1
    mongod.exe --dbpath f:\mongodb\db --logpath 、f:\mongodb\logs\db.log --install --serviceName mongodb --logappend --directoryperdb
  4. 启动mongodb服务
    cmd命令输入

    1
    net start mongodb

配置文件形式启动数据库

在f:\mongodb\目录新建配置文件mongodb.init,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#数据库
dbpath=F:\mongodb\data
directoryperdb = true


#日志
logpath=F:\mongodb\logs\mongod.log
#错误日志采用追加模式
logappend=true
#启用日志模式,默认启用
journal=true
#这个选项可以过滤掉一些无用的日志信息,若需要调试使用,请设置为false
quiet=true
#cpu=true
#verbose = true

# 认证
#autho =false

# 管理
#reset = true

# 主节点配置
master = true

# 子节点配置
#slave = true
#source = 192.168.3.56
#only = xxx

#端口号 默认27017
port=27017

在f:\mongodb\目录新建启动文件install.bat,启动服务的命令

1
mongod -f f:\mongodb\config.ini --install --serviceName "mongodb"

对应的,新建uninstall.bat,关闭服务的命令

1
mongod --remove

然后执行

1
net start mongodb

注意事项

如果您的MongoDB服务总是无法启动,或启动后一直在反复重启,那么请检查data目录下是否存在一个名为“mongod.lock”的文件,如果存在就删除它,然后重启服务即可。这种情况通常是由于MongoDB服务没有正常关闭造成的,比如意外断电。

重点
通过实践,发现在windows环境下表,使用net start mongodb
启动非常不稳定,因此建议使用如下脚本命令,新建start.bat文件

1
2
del f:\mongodb\data\mongod.lock
mongod.exe --dbpath=f:\mongodb\data --logpath=f:\mongodb\logs\db.log

然后再启动一个dos,输入mongo,进入操作界面

基本操作

创建数据库,不存在,创建,存在则切换

1
user 'dbname`

查看所有数据库
show dbs

往student数据库插入一条数据,json形式
db.student.insert({“name”:”hammercui”})

删除数据库,之前先切换到当前数据库
db.dropDatabase()

集合删除
db.collection.drop()

student数据库的socre集合插入一个数据/文档
db.score.insert({yuwen:100,shuxue:100,lishi:100})

查询文档/数据
db.score.find()

更新文档/数据
db.score.update({条件},{新值})

python学习笔记:5 web开发

发表于 2016-09-12   |   分类于 python   |  

wsgi

wsgi是最基础的接口实现方式,

1
2
3
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return '<h1>Hello, web!</h1>'

上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

  • environ:一个包含所有HTTP请求信息的dict对象;

  • start_response:一个发送HTTP响应的函数。

运行wsgi服务器,server.py

1
2
3
4
5
6
7
8
9
10
11
# server.py
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server
# 导入我们自己编写的application函数:
from hello import application

# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print "Serving HTTP on port 8000..."
# 开始监听HTTP请求:
httpd.serve_forever()

但是维护起来太蛋疼了,我们需要引入web服务框架

有了Web框架,我们在编写Web应用时,注意力就从WSGI处理函数转移到URL+对应的处理函数,这样,编写Web App就更加简单了。

Flask:精简型web框架

Django:全能型Web框架;

web.py:一个小巧的Web框架;

Bottle:和Flask类似的Web框架;

Tornado:Facebook的开源异步Web框架。

模板

协程

跟c#的协程一样,一种子程序可以中断的机制。
协程的优点:

  1. 协程跟多线程比,没有线程切换的开销,协程只有一个线程。
  2. 其次,协程最大的优势是不需要多线程的锁机制。

所以在多核CPU的逻辑是,多进程+协程。
Python对协程的支持还非常有限,用在generator中的yield可以一定程度上实现协程。虽然支持不完全,但已经可以发挥相当大的威力了。

还记得我们学习生成器时(generator),使用的yield关键字吗,如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

以下举例,利用协程实现,无锁状态下的生产者,消费者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time

def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
time.sleep(1)
r = '200 OK'

def produce(c):
c.next()
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()

if __name__=='__main__':
c = consumer()
produce(c)

gevent

Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。

python学习笔记:4 常用模块

发表于 2016-09-09   |   分类于 python   |  

常用内建模块

collections

namedtuple
用来自定义tuple。

1
2
3
4
5
from collections import nametuple

Point = nametuple('Point',['x','y'])
p = Point(1,2)
pring p.x,p.y

deque
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈

1
2
3
from collections import deque
q = deque(['a','b','c'])
a.append('xx)

defaultdict
使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict

OrderedDict

如果要保持Key的顺序,可以用OrderedDict:
注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:

base64

1
2
3
4
5
>>> import base64
>>> base64.b64encode('binary\x00string')
'YmluYXJ5AHN0cmluZw=='
>>> base64.b64decode('YmluYXJ5AHN0cmluZw==')
'binary\x00string'

hashlib

Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)

md5使用

1
2
3
4
5
import hashlib

md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?')
print md5.hexdigest()

sha1使用

1
2
3
4
5
6
import hashlib

sha1 = hashlib.sha1()
sha1.update('how to use sha1 in ')
sha1.update('python hashlib?')
print sha1.hexdigest()

常用第三方模块

正则表达式

发表于 2016-09-09   |   分类于 基础   |  

正则表达式广泛用于前端,移动端,以及服务端,用来做字符串的规则校验,校验是否合法

  • \d可以匹配一个数字,\w可以匹配一个字母或数字
1
2
3
'00\d'可以匹配'007',但无法匹配'00A';

'\d\d\d'可以匹配'010';
  • .匹配任意字符
  • *表示任意个字符,包括0个,+表示至少一个字符,?表示0个或1个字符,{n}表示n个字符,{n-m}表示n-m个字符
  • []表示范围
    1
    2
    3
    4
    5
    6
    7
    8
    [0-9a-zA-Z\_]可以匹配一个数字,字母或者下划线
    [0-9a-zA-Z\_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','Py3000'等等
    ```
    * `A|B`可以匹配A或B,所以`(P|p)ython`可以匹配`'Python'`或者`'python'`
    * ^表示行的开头,^\d表示必须以数字开头。
    * $表示行的结束,\d$表示必须以数字结束。

    ## python中的使用

import re
if re.match(r’^\d{3}-\d{3,8}$’,’010-55’):
print ‘ok’
else:
print ‘not match’

1
2

无法识别连续的空格,用正则表达式试试:

re.split(r’\s+’, ‘a b c’)
[‘a’, ‘b’, ‘c’]

1
2

无论多少个空格都可以正常分割。加入,试试

re.split(r’[\s\,]+’, ‘a,b, c d’)
[‘a’, ‘b’, ‘c’, ‘d’]

1
再加入;试试:

re.split(r’[\s\,\;]+’, ‘a,b;; c d’)
[‘a’, ‘b’, ‘c’, ‘d’]

1
2
**分组功能**
加上()用来做分组的拆分

m = re.match(r’^(\d{3})-(\d{3,8})$’, ‘010-12345’)
m.group(1)
‘010’
m.group(2)
‘12345’

1
2
3

**预编译**
如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式

import re

编译:

re_telephone = re.compile(r’^(\d{3})-(\d{3,8})$’)

使用:

re_telephone.match(‘010-12345’).groups()
执行结果:
(‘010’, ‘12345’)
```

python学习笔记:3 io操作

发表于 2016-09-07   |   分类于 python   |  

文件读写

可以使用open来操作文件,但是每次都要close

1
2
3
4
5
6
try:
f = open('iotest.txt', 'rb')
print f.read()
finally:
if f:
f.close()

所以推荐使用with ,能自动完成close

1
2
with open('iotest.txt', 'rb') as f:
print f.read().decode('gbk')

练习:遍历目录下所有文件,查找文件名包含test的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
absolute_path = sys.path[0]
#print absolute_path
def search(path,name):
for x in os.listdir(path):
fullname = os.path.join(path,x)
if os.path.isfile(fullname):
#print '文件:%s' % fullname
if name in x:
print '选中:%s' % fullname
else:
search(fullname,name)

search(absolute_path,'test')

序列化

我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思

序列化文件:

1
2
3
f = open('dump.txt', 'wb')
pickle.dump(dic, f)
f.close()

反序列化文件

1
2
3
f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()

字典序列化为json

1
2
3
str_json = json.dumps(dic)
print str_json
print json.loads(str_json)

对象序列化为json

1
2
3
4
5
6
7
8
9
10
class Student(object):
def __init__(self,name,age,score):
self.name = name
self.age = age
self.score = score
def student2dict(self,object):
return {'name':self.name,'age':self.age,'score':self.score}

hammer = Student('hammer',27,100)
print json.dumps(hammer,default=hammer.student2dict)

多进程

unix/lunix使用fork(),会复制当前进程出一个子进程

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:
Progress
使用Progress来写快平台多进程

1
2
3
4
5
6
7
8
9
10
11
12
# 1 使用Process来创建进程
def run_proc(name):
print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__=='__main__':
print 'Parent process %s.' % os.getpid()
p = Process(target=run_proc, args=('test',))
print 'Process will start.'
p.start()
p.join()
print 'Process end.'
####################

Pool
使用Pool进程池,来批量创造进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#2 学习使用Pool,进程池
def long_time_task(name):
print '执行 tast %s(%s)...' % (name,os.getpid())
start = time.time()
time.sleep(random.random() * 3 )
end = time.time()
print 'Task %s run %0.2f seconds.' % (name,end - start)

if __name__ =='__main__':
print '父进程id %s .' % os.getpid()
p = Pool()
for i in range(5):
p.apply_async(long_time_task,args=(i,))
print '等待所有子进程执行完毕.'
p.close() #close之后Pool不能新增子进程了
p.join() # join方法会等待所有子进程执行完毕
print 'All done .'
#########################

进程间通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 3 学习使用Queue,实现进程间通信
# 写数据进程执行的代码:
def write(q):
for value in ['A', 'B', 'C']:
print 'Put %s to queue...' % value
q.put(value)
time.sleep(random.random())

# 读数据进程执行的代码:
def read(q):
while True:
value = q.get(True)
print 'Get %s from queue.' % value

if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()

多线程

Python是真正的多线程。
Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。
意思是Python虽然是实际设计的多线程,但是由于全局所的存在,无法使用CPU的多核性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 学习使用多线程,主要是threading
import time,threading
def loop():
print 'thread %s is running ...' % threading.current_thread().name
n = 0
while n < 5:
n = n+1
print 'thread %s >>> %s' % (threading.current_thread().name,n)
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop,name = 'ChildThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name

选择进程还是线程

如果用多进程实现Master-Worker,主进程就是Master,其他进程就是Worker。

如果用多线程实现Master-Worker,主线程就是Master,其他线程就是Worker。

  • cpu密集型:典型的是计算圆周率,视频解码
  • io密集型:典型的是web服务器

    如果充分利用操作系统提供的异步IO支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型,Nginx就是支持异步IO的Web服务器。它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上,可以运行多个进程(数量与CPU核心数相同)。
    python单进程的异步编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序.
    类似于nodejs服务器,也是采用的基于事件驱动的单进程单线程来执行多任务。

分布式进程

为什么选用Process,而不是Thread呢,因为进程可以部署到不同的机器上,形成分布式进程。
比如:
机器1》 创建Queue,把Queue注册到网络上暴露,供其他进程访问

python学习笔记:2.面向对象

发表于 2016-09-05   |   分类于 python   |  

模块

一个py文件,就是一个模块(module),相同函数和变量名可以存在不同的模块中。一个py模块就是一个类,可以这么理解

为了避免类的冲突,引入包的概念。注意每一个包下面,都会有一个init.py的文件,他的模块名就是包名

作用域

  • 正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;

  • 类似_xxx和xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,abc等;

之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。

命令行运行
在命令行运行py文件时,Python解释器把一个特殊变量name置为main
因此以下代码

1
2
if __name__=='__main__':
test()

在$ python hello.py运行时,会执行test(),但是import时不会执行

安装第三方模块

安装pip
使用pip安装,可在命令行检查输入pip,检查是否安装。
如果安装不成功,可以直接去官网地址下载

下载完成之后,解压到一个文件夹,用CMD控制台进入解压目录,输入:

1
python setup.py install

然后输入pip命令,检测是否安装成功

安装PIL
PIL是一个图片处理库,而且是跨平台的。
当我们执行以下失败时

1
pip install PIL

换成

1
pip install pillow

然后引入时fom PIL import Image

在python2中使用python3

1
2
3

from __future__ import unicode_literals
from __future__ import division

2.x里的字符串用’xxx’表示str,Unicode字符串用u’xxx’表示unicode
在3.x中,所有字符串都被视为unicode,写u’xxx’和’xxx’是完全一致的,但是str就必须写成b’xxx’,来表示“二进制字符串”

面向对象编程

给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。这里指明了方法跟函数概念上的区别

数据封装、继承和多态是面向对象的三大特点。

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

继承和多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#父类
class Animal(object):
def run(self):
print "Animal is running"

class Dog(Animal):
pass
class Cat(Animal):
pass

dog = Dog()
dog.run()
cat = Cat()
cat.run()

  • type(),来判断对象类型。

  • 通过内置的一系列函数,我们可以对任意一个Python对象进行剖析,拿到其内部的数据。

  • dir(),来获得一个对象的所有属性跟方法。

高级特性:多重继承,定制类,元类

动态绑定
动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现

以下给一个实例绑定方法,其他实例没有这个方法:

1
2
3
4
5
6
7
8
# 给dog对象动态新增方法set_age

def set_age(self,age): #定义一个函数作为实例方法
self.age = age

dog.set_age = MethodType(set_age,dog,Dog)# 给实例绑定一个方法
dog.set_age(25)
print 'dog的age:%s' % dog.age

以下是给对象绑定方法,它的所有实例都会有这个方法

1
2
3
4
5
6
7
8
def set_price(self,price): #定义一个函数作为实例方法
self.price = price

Animal.set_price = MethodType(set_price,None,Animal)# 给类绑定一个方法
dog.set_price('200$')
cat.set_price('300$')
print dog.price
print cat.price

@property
类似java的get set,避免把属性直接暴露

1
2
3
4
5
6
7
8
9
class Student(object):

@property
def birth(self):
return self._birth

@birth.setter
def birth(self, value):
self._birth = value

多重继承

区别于java的单继承

1
2
3
4
class Dog(Mammal, RunnableMixin, CarnivorousMixin):
pass

这样Dog类就继承了Mammal类,RunnableMixin类,CarnivorousMixin类

定制类
__str__
__repr__
用法:

1
2
3
4
5
6
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错误时退出循环。

__getattr__
利用完全动态的getattr写一个链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
class Chain(object):
# class实例化调用
def __init__(self,path = ''):
self._pathh = path
# 调用Chain不存在的属性时
def __getattr__(self, path):
return Chain('%s/%s' % (self._pathh,path))
def __str__(self):
return self._pathh

print Chain().statu.v2.member.center
结果
/statu/v2/member/center

__call()__可以使对象被当成函数进行调用

使用元类

  • type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的
  • type()函数既可以返回一个对象的类型,又可以创建出新的类型

    1
    2
    3
    4
    5
    def fn(self, name='world'): # 先定义函数
    print('Hello, %s.' % name)
    Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
    h = Hello()
    h.hello()
  • metaclass
    控制类的创建行为,还可使使用这个,元类
    先定义metaclass,就可以创建类,最后创建实例。所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

调试

  • try catach
  • assert 断言,类似于print,输出内容
  • logging.info(),可以指定输出的级别
    1
    2
    import logging
    logging.basicConfig(level=logging.INFO)

单元测试
被测试文件mydict.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding:utf-8 -*-
# 单元测试
class Dict(dict):

def __init__(self, **kw):
super(Dict, 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

我们需要写的测试代码mydict_test.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# -*- coding:utf-8 -*-
# 进行单元测试
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):

def test_init(self):
d = Dict(a=1, b='test')
self.assertEquals(d.a, 1)
self.assertEquals(d.b, 'test')
self.assertTrue(isinstance(d, dict))

def test_key(self):
d = Dict()
d['key'] = 'value'
self.assertEquals(d.key, 'value')

def test_attr(self):
d = Dict()
d.key = 'value'
self.assertTrue('key' in d)
self.assertEquals(d['key'], 'value')

def test_keyerror(self):
d = Dict()
with self.assertRaises(KeyError):
value = d['empty']

def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty

def setUp(self):
print 'setUp...开始测试'

def tearDown(self):
print 'tearDown...测试结束'

python学习笔记:1.基础

发表于 2016-08-31   |   分类于 python   |  

对于大多数程序员来说,有了一定功底,就会觉得语言大同小异,再学习动态语言,也不会觉得是难事。因此,我只做一些简单的记录,记录会跟静态语言混淆的地方。

数据类型,变量

  • python中同一个变量,可以反复赋值,而且可以是不同类型的变量。这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错,笔记java.

    1
    2
    3
    4
    a = 123 # a是整数
    print a
    a = 'ABC' # a变为字符串
    print a
  • 通常用全部大写,表示常量。Python根本没有任何机制保证PI不会被改变,所以,用全部大写的变量名表示常量只是一个习惯上的用法

list

  • Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素
    用索引来访问list中每一个位置的元素,记得索引是从0开始的,这点跟lua有区别

    1
    2
    3
    nameList = ['xiaoming','xiaoli','xiaozhang']
    print nameList
    print len(nameList)
  • 如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素,以此类推,可以获取倒数第2个、倒数第3个:

    1
    2
    nameList[-1]
    nameList[-2]
  • 要删除list末尾的元素,用pop()方法

  • 要删除指定位置的元素,用pop(i)方法,其中i是索引位置

tuple

元组,跟list的区别是,元组是不能修改的,不能赋值成其他的元素,

1
nameList = ('xiaoming','xiaoli','xiaozhang')

dic

字典,普遍用法

  • 判断key是否存在

    1
    2
    3
    'key' in dict
    或者
    dict.get('key')
  • 删除key:pop(key)

set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
s = set([1, 2, 3])

  • 重复元素在set中自动被过滤:

    1
    2
    3
    >>> s = set([1, 1, 2, 2, 3, 3])
    >>> s
    set([1, 2, 3])
  • 通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果

  • 通过remove(key)方法可以删除元素:

  • set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:
    1
    2
    3
    4
    5
    6
    >>> s1 = set([1, 2, 3])
    >>> s2 = set([2, 3, 4])
    >>> s1 & s2
    set([2, 3])
    >>> s1 | s2
    set([1, 2, 3, 4])

字符编码

  • 支持中文,需要在开头添加
1
# -*- coding: utf-8 -*-
  • 在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:
1
2
3
4
5
6
7
8
9
a = 'Hi ,%s,you have 钱数:%d' % ('Hammercui',10000)
print a

常见的占位符有:

%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数

函数

  • abs()绝对值函数
  • cmp(x,y)比较函数
  • int() 转int函数
  • 声明函数:def,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。
1
2
3
4
5
6
7
def my_abs(x):
if x>0:
return x
else:
return -x

print my_abs(-2000)
  • 默认参数
1
2
3
4
5
6
7
8
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s

power(5)
  • fact(n)阶乘函数。递归不做优化的话,会导致栈溢出

slice切片操作符

L[0:3]表示从list L中取出索引0到3,不包括3的元素,返回list
前10个数,每两个取一个:

1
2
 L[:10:2]
[0, 2, 4, 6, 8]

  • 字符串’xxx’或Unicode字符串u’xxx’也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:
1
2
3
4
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'

迭代

  • 判断是否能是迭代

    1
    2
    3
    4
    5
    6
    7
    >>> from collections import Iterable
    >>> isinstance('abc', Iterable) # str是否可迭代
    True
    >>> isinstance([1,2,3], Iterable) # list是否可迭代
    True
    >>> isinstance(123, Iterable) # 整数是否可迭代
    False
  • 如果要对list实现类似Java那样的下标循环怎么办?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

    1
    2
    3
    # 模仿java写法
    for i,value in enumerate(['a','b','c']):
    print i,value

列表生成式

但如果要生成[1x1, 2x2, 3x3, …, 10x10]怎么做?方法一是循环,方法二如下:

1
2
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

可以增加条件

1
2
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

还可以双层循环

1
2
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

生成器generator

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)

定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。这样就保证了,每次next都是从上次的结果之后执行的。

高阶函数

指函数名也是变量,可以把函数作为参数传递。
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式

map函数

接收两个参数,第一个函数,第二个序列(可以是list,tuple),返回结果list
比如,把一个int类型的list转成string类型的list

1
2
3
map(str,[1,2,3,4])
结果:
['1','2','3','4']

reduce函数

接收两个参数,第一函数,但这个函数必须接收2个参数,第二个序列,返回list。
reduce与map的区别是

1
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

相当于每次传入2个参数,把结果作为新的参数,跟下一个参数再传入

filter函数

传入两个参数,和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

例如,在一个list中,删掉偶数,只保留奇数,可以这么写:

1
2
3
4
5
6
def is_odd(n):
return n % 2 == 1

filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])
结果
# 结果: [1, 5, 9, 15]

sorted函数

与其他相反,序列在前,函数在后

匿名函数

比如函数

1
2
def f(x)
return x*x

可以写作
lambda x:x*x
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

装饰器

假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

偏函数

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

比如二进制转换成int

1
2
3
4
5
6
def int2(x, base=2):
return int(x, base)

int2('1000000')
结果
64

使用偏函数写法就是

1
2
3
4
5
import functools
int2 = functools.partical(int,base=2)
int2('1000000')
结果
64

android最佳实践9-2:greenDao的数据库升级

发表于 2016-08-01   |   分类于 android   |  

app开发期间的数据库升级

我们使用DevOpenHelper打开数据库

1
2
 DaoMaster.DevOpenHelper helper = 
new DevOpenHelper(application,"xue_old.db",null);

DevOpenHelper内容

1
2
3
4
5
6
7
8
9
10
11
12
public static class DevOpenHelper extends OpenHelper {
public DevOpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
}

其中

1
2
3
4
//删除当前所有表
dropAllTables(db, true);
//新建当前所有表
onCreate(db);

这种方式意味着每次都重新创建,所以数据不会报错

app 上线后的数据库升级,Migration(合并)方式

我们自己新建一个HMROpenHelper,继承自OpenHelper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HMROpenHelper extends DaoMaster.OpenHelper {

public HMROpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}

/**
* 数据库升级
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//操作数据库的更新
MigrationHelper.migrate(db,NoteDao.class,LessonDao.class,ZhangDao.class);
}

}

数据库的合并如下
MigrationHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
public class MigrationHelper {

/**
* 调用升级方法
* @param db
* @param daoClasses 一系列dao.class
*/
public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
//1 新建临时表
generateTempTables(db, daoClasses);
//2 创建新表
createAllTables(db, false, daoClasses);
//3 临时表数据写入新表,删除临时表
restoreData(db, daoClasses);
}


/**
* 生成临时表,存储旧的表数据
* @param db
* @param daoClasses
*/
private static void generateTempTables(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
//方法2
for (int i=0;i<daoClasses.length;i++){
DaoConfig daoConfig = new DaoConfig(db,daoClasses[i]);
String tableName = daoConfig.tablename;
if (!checkTable(db,tableName))
continue;
String tempTableName = daoConfig.tablename.concat("_TEMP");
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("alter table ")
.append(tableName)
.append(" rename to ")
.append(tempTableName)
.append(";");
db.execSQL(insertTableStringBuilder.toString());
}
}

/**
* 检测table是否存在
* @param db
* @param tableName
*/
private static Boolean checkTable(SQLiteDatabase db,String tableName){
StringBuilder query = new StringBuilder();
query.append("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='").append(tableName).append("'");
Cursor c = db.rawQuery(query.toString(), null);
if (c.moveToNext()){
int count = c.getInt(0);
if(count>0){
return true;
}
return false;
}
return false;
}

/**
* 删除所有旧表
* @param db
* @param ifExists
* @param daoClasses
*/
private static void dropAllTables(SQLiteDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "dropTable", ifExists, daoClasses);
}

/**
* 创建新的表结构
* @param db
* @param ifNotExists
* @param daoClasses
*/
private static void createAllTables(SQLiteDatabase db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "createTable", ifNotExists, daoClasses);
}

/**
* 创建根删除都在NoteDao声明了,可以直接拿过来用
* dao class already define the sql exec method, so just invoke it
*/
private static void reflectMethod(SQLiteDatabase db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
if (daoClasses.length < 1) {
return;
}
try {
for (Class cls : daoClasses) {
//根据方法名,找到声明的方法
Method method = cls.getDeclaredMethod(methodName, SQLiteDatabase.class, boolean.class);
method.invoke(null, db, isExists);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

/**
* 临时表的数据写入新表
* @param db
* @param daoClasses
*/
private static void restoreData(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
if (!checkTable(db,tempTableName))
continue;
// get all columns from tempTable, take careful to use the columns list
List<String> columns = getColumns(db, tempTableName);
//新表,临时表都包含的字段
ArrayList<String> properties = new ArrayList<>(columns.size());
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (columns.contains(columnName)) {
properties.add(columnName);
}
}
if (properties.size() > 0) {
final String columnSQL = TextUtils.join(",", properties);

StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
}
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(dropTableStringBuilder.toString());
}
}

private static List<String> getColumns(SQLiteDatabase db, String tableName) {
List<String> columns = null;
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
if (null != cursor && cursor.getColumnCount() > 0) {
columns = Arrays.asList(cursor.getColumnNames());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
if (null == columns)
columns = new ArrayList<>();
}
return columns;
}
}

其核心思路是
1 把旧表改为临时表
2 建立新表
3 临时表数据写入新表,删除临时表

这样我们就可以直接打开数据库进行更新操作了

1
2
3
HMROpenHelper helper = new HMROpenHelper(application, "xue_old.db", null);
SQLiteDatabase sqlDB = helper.getWritableDatabase();
return sqlDB;

重点

别忘了更新时SCHEMA_VERSION自动加1,不然不会执行更新
位于DaoMaster.java

android最佳实践11:butterknife框架使用

发表于 2016-07-28   |   分类于 android   |  

介绍

butterknife也是一个依赖注入框架,借助annonation实现view的快速初始化,解除findViewById的烦恼

工程引入

配置project的build.gradle,来引入android-apt插件

1
2
3
4
5
6
7
8
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}

配置module的build.gradle,apply the android-apt plugin,and add the Butter Knife dependencies.

1
2
3
4
5
6
7
8
9
10
11

apply plugin: 'android-apt`

android{
...
}

dependencies{
compile 'com.jakewharton:butterknife:8.2.1'
apt 'com.jakewharton:butterknife-compiler:8.2.1'
}

注意
butter knife尽量不要在library中使用,如果要用,还得转R2,太麻烦了

使用

使用@BindView注解view

1
2
3
4
5
6
7
8
9
10
11
12
class ExampleActivity extends Activity {
@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.footer) TextView footer;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}

Resource的注解

@BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString

1
2
3
4
5
6
7
class ExampleActivity extends Activity {
@BindString(R.string.title) String title;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@BindColor(R.color.red) int red; // int or ColorStateList field
@BindDimen(R.dimen.spacer) Float spacer; // int (for pixel size) or float (for exact value) field
// ...
}

non-activity的注解

1
2
3
4
5
6
7
8
9
10
11
public class FancyFragment extends Fragment {
@BindView(R.id.button1) Button button1;
@BindView(R.id.button2) Button button2;

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
}

也可以在ViewHolder中使用

View List

1
2
@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;

使用apply方法,能一次操作整个View的List

1
2
ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);

包括设置属性

1
ButterKnife.apply(nameViews, View.ALPHA, 0.0f);

Listener的绑定

1
2
3
4
@OnClick(R.id.submit)
public void submit(View view) {
// TODO submit data to server...
}

绑定的释放

由于Fragment的生命周期区别于activity,当我们在Fragment的onCreatView做绑定的时候,要在onDestoryView中设置views为null。Butter Knife 返回一个Unbider实例来解决这个问题,
eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FancyFragment extends Fragment {
@BindView(R.id.button1) Button button1;
@BindView(R.id.button2) Button button2;
private Unbinder unbinder;

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
unbinder = ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}

@Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

更懒的方式

使用Zelezny插件,一键生成view

如何找到插件

参考文献

github地址
文档介绍·英文

android最佳实践7:androidannotations框架

发表于 2016-06-17   |   分类于 android   |  

AndroidAnnotations

android依赖注入框架,主要作用于view层。

工程引入

android studio环境

工程的build.gradle如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
buildscript {
repositories {
mavenCentral()
}
dependencies {
···
// replace with the current version of the android-apt plugin
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}

repositories {
mavenCentral()
mavenLocal()
}

app/build.gradle如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apply plugin: 'android-apt'
def AAVersion = 'XXX'

dependencies {
...
apt "org.androidannotations:androidannotations:$AAVersion"
compile "org.androidannotations:androidannotations-api:$AAVersion"
...
}

apt {
arguments {
androidManifestFile variant.outputs[0]?.processResources?.manifestFile
// if you have multiple outputs (when using splits), you may want to have other index than 0

// you should set your package name here if you are using different application IDs
// resourcePackageName "your.package.name"

// You can set optional annotation processing options here, like these commented options:
// logLevel 'INFO'
// logFile '/var/log/aa.log'
}
}

具体使用

Android开源框架(一):AndroidAnnotations

123…7
hammercui

hammercui

69 日志
13 分类
19 标签
RSS
github
Links
  • logicool的技术专栏
  • xiekw2010的专栏
  • 江清清的技术专栏
© 2017 hammercui
由 Hexo 强力驱动
主题 - NexT.Pisces