简单的解压列表和元组就省略,如果在解压时想要忽略一个元素,之前我们知道可以使用 _
来忽略
first, _ = ("Ein", "Verne")
这是第二个元素不关心,也就不取了,但是如果要忽略一批元素呢
>>> record = ('ACME', 50, 3.14, (06,04,1989))
>>> name, *_, (*_, year) = record
这时就可以批量忽略中间的 50, 3.14 还有括号中的月份日期了。
之前在看 slice 相关的内容的时候只简单的看了类似 list[:5] 这样的切片操作,几天突然朋友问道下面三元的切片操作,竟然一时没有反应过来,去看了一下文档,原来 slice 可以支持三个参数 obj[start:stop:step],那么综合之前学习的内容就很好解释了。
先来复习一下,假设 l = list(range(10))
,那么
l[1:3] # 结果 [1,2] 左边包括,右边不包括
l[:5] # [0,1,2,3,4] 不包括 index 为 5 的值
l[5:] # [5,6,7,8,9]
l[-5:] # 取后 5 个 [5,6,7,8,9]
l[:-4] # 除了后四位,输出 [0,1,2,3,4,5]
所以只有 l[start:end] 两个参数的很好理解,只需要直到切片操作是负数表示的序列中的 index 就行。
如果加上 step,那就是 l[start:end:step],而对于正数 step 也很好理解:
l[1:5:2] # 从第 1 位到第 5 位,不包括 5 位,每次前进 2 个,结果 [1,3]
l[:5:1] # 从第 0 个取到第 5 个,不包括第 5 位,每次前进 1,输出 [0,1,2,3,4]
l[5::1] # 从 index 5 取值到最后,每次进一位,输出 [5,6,7,8,9]
再来看下 step 为负数的情况
l[::-1] # 逆序输出所有的元素
l[-1:-5:-1] # 第 -1 位到第 -5 位,不包括第 -5 位,每次往前一位,所以为 [9,8,7,6]
l[-1::-1] # 从最后一位开始,每次往前一位,直到最前面 输出 [9,8,7,6,5,4,3,2,1,0]
l[5::-1] # 可以怎么理解,从序号 5 开始,每次往前一位,直到最后,所以输出 [5,4,3,2,1,0]
如果 start 没有被省略,那么找到 start,然后往前查找直到 stop 就行,而如果 start 被省略,则需要从最末尾往前:
l[:5:-1] # 因为省略了开头,所以需要看 step 正负,这里为 -1,所以从最后一位往前直到 index 为 5,所以输出 [9,8,7,6]
这样就比较清除的解决这些问题了,如果 step 换成 2 ,也是一样的模式。
python 文件处理时会遇到 open("filename", "mode")
这个函数后面的参数模式:
r
只读模式打开文件,文件指针在文件开头rb
以二进制读,文件指针在文件开头r+
读写模式 (cannot truncate a file),文件指针在文件开头rb+
以二进制文件读或者写,文件指针在文件开头w
以写模式打开文件,只写入,任何同名文件会被覆盖,如果文件不存在会创建新文件写入w+
读写模式 (can truncate a file)wb
以二进制模式写,同名文件覆盖,不存在创建新文件wb+
以二进制模式读写,同名文件覆盖,不存在创建a
附加模式,在文件末增加,文件指针在文件末尾,如果文件不存在会创建新的文件写ab
以二进制形式附加,文件指针在末尾a+
附加,和读 打开文件,指针在文件末尾ab+
以二进制打开文件读或者附加,如果文件存在,文件指针指向文件末尾x
python 3 中新模式,如果文件存在会创建失败所以可以总结一些规律
+
如果放在 r
后,是读写,放在 w
后也是 读写,所以有 +
模式表示读和写例子
# Python 2
>>> print type("Hello World!")
<type 'str'>
# this is a byte string
>>>print type(u"Hello World!")
<type 'unicode'>
# this is a Unicode string
Python 3
# Python 3
>>> print(type("Hello World!"))
<class 'str'>
# this is a Unicode string
>>> print(type(b"Hello World!"))
<class 'bytes'>
# this is a byte string
总而言之,Python 2 中字面 str 以 bytes 存储,前缀 u’hello’ 表示 unicode
对象,以 unicode
存储。
而 Python 3,字面 str 以 Unicode 存储,前缀 b’hello’ 表示获取 bytes 对象,或者 str.encode()
来获取 bytes 对象。
Python 3.x 中有三种 string 对象类型,一种是文本类型,另外两种是二进制数据
str
表示 Unicode 文本 (both 8bit and wider)bytes
表示二进制内容bytearray
,一种可变的 bytes
类型类型转换
将 string 转换为 raw bytes
str.encode()
或者 str(B, encoding)
将 raw bytes 转变为 str
bytes(S, encoding)
或者 bytes.decode()
在处理 Unicode 编码文本,Python 支持
在处理 ASCII 编码时
>>> ord('X')
88
>>> chr(88)
'X'
>>> S = 'XYZ'
>>> S.encode('ascii') # Values 0..127 in 1 byte (7 bits) each
>>> S.encode('latin-1') # Values 0..255 in 1 byte (8 bits) each
S.encode('utf-8') # Values 0..127 in 1 byte, 128..2047 in 2, others 3 or 4
在处理非 ASCII 编码时,可能会用到之前提到的 Unicode 编码
>>> chr(0xc4) # 0xC4, 0xE8: characters outside ASCII's range
>>> S = '\xc4\xe8' # Single byte 8-bit hex escapes
>>> S = '\u00c4\u00e8' # 16-bit Unicode escapes
>>> len(S) # 2 characters long (not number of bytes!)
如果我们使用 encode
来将一个非 ASCII 字符串使用 ASCII 编码转变为 raw bytes ,会抛出错误。
>>> S = '\u00c4\u00e8'
>>> S.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1:
ordinal not in range(128)
>>> S.encode('latin-1') # One byte per character
b'\xc4\xe8'
>>> S.encode('utf-8') # Two bytes per character
b'\xc3\x84\xc3\xa8'
>>> len(S.encode('latin-1')))
2
>>> len(S.encode('utf-8'))
4
在 Python 中如果一个类继承自多个类,这种行为被称为多重继承 multiple inheritance,虽然多重继承非常有用,但是需要注意一些问题,否则会出现不可预见的问题。
在使用多重继承时,如果一个方法从多个超类中继承,先继承的类中的方法会重写后继承类中的方法。(超类顺序为定义 class 时的顺序)。
@property
是一个将类方法伪装成为一个属性。
from math import pi
class Circle():
def __init__(self,r):
self.r = r
@property
def area(self):
return pi*self.r**2
c = Circle(2)
print(c.area)
在这里 area()
虽然被定义为一个方法,但是类的实例可以使用点来访问 area
,假装是类的一个属性。
Python 2.4 中,引入了装饰器(decorators) 的语法,能够对任何可调用的对象进行包装,既能够用于方法也能够用于函数。使用 @
操作符,在方法或函数上将装饰器列出,指定一个或者多个装饰器。多个装饰器在应用时的顺序与指定顺序相反。
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')
classmethod
必须有一个指向 class object 的 reference 作为第一参数,而 staticmethod 则不需要。classmethod 通常被用来作为构造函数重载。
C++ 有重载的功能,但是 Python 缺乏重载的机制,所以就有了 classmethod
,可以想象成另外一个构造函数
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
date2 = Date.from_string('11-09-2012')
cls
是一个持有 class self 的对象,但是不是 class 的一个实例。如果继承了 Date
类,所有的子类都会拥有 from_string
方法。
对于这个例子,is_date_valid
方法不需要关心类内部的其他变量和方法,但是这个 valid 方法和 Date 相关,可以定义为静态方法。和其他语言中的静态方法可以一起理解。
拦截 intercept 对象的所有特性访问是可能的,然后有一些魔法方法。比如 __getattr__
和 _setattr__
__getattribute__(self.name)
当特性 name 被访问时自动被调用,只能在新式类中使用__getattr__(self.name)
当特性 name 被访问且对象没有相应的特性时被自动调用__setattr__(self.name.value)
当试图给特性 name 赋值时被自动调用__delattr__(self.name)
当试图删除特性 name 时被自动调用这里是一些区别
__getattribute__
无条件被调用,任何对象的属性访问时都会隐式调用,比如 t.__dict__
其实是执行了 t.__getattribute__("__dict__")
,所以如果我们在重载__getattribute__
中又调用__dict__
的话,会无限递归,用 object 来避免,即 object.getattribute(self, name)__getattr__
只有 __getattribute__
找不到的时候才会调用 __getattr__
举例
class Attr(object):
def __init__(self, name):
self.name = name
def __getattribute__(self, item):
print("__getattribute__ " + item)
return object.__getattribute__(self, item)
def __getattr__(self, item):
print("__getattr__ " + item)
def __setattr__(self, key, value):
print("__setattr__ key %s, value %s" % (key, value))
object.__setattr__(self, key, value)
if __name__ == '__main__':
attr = Attr('wendy')
print("main " + attr.name)
attr.name = 'nancy'
在这个例子中,输出结果
__setattr__ key name, value wendy
__getattribute__ name
main wendy
__setattr__ key name, value nancy
在 Python 中,很多对象都是可以通过 for 语句来直接遍历的,例如 list、string、dict 等等,这些对象都可以被称为可迭代对象。迭代器对象要求支持迭代器协议的对象,在 Python 中,支持迭代器协议就是实现对象的__iter__()
和 next()
方法。其中__iter__()
方法返回迭代器对象本身;next()
方法返回容器的下一个元素,在结尾时引发 StopIteration 异常。
class PowTwo:
"""Class to implement an iterator
of powers of two"""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
p = PowTwo(5)
for i in p:
print(i)
生成器通过生成器函数产生,生成器函数可以通过常规的 def 语句来定义,但是不用 return 返回,而是用 yield 一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。
在理解生成器之前,首先要先理解上面提到的迭代器,使用迭代器能够非常轻松的遍历列表等等中的内容,虽然迭代器很好用,但是迭代器会将内容全都放到内存中,所以一旦遇到特别庞大的列表,可能就会遇到问题。
所以有了生成器,生成器是一种特殊的迭代器,只能够遍历一次。
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
这里 mygenerator 需要使用 ()
。
然后 yield
关键字,就像 return
,但是表示方法会返回一个生成器。
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
返回 yield
的方法在方法被调用的时候并不会执行,只有在使用 for
来遍历该生成器时,才会执行。执行的顺序是,方法每 yield 一次就返回一次,等待 for 中执行完毕,继续执行生成器方法中的下一次 yield,然后返回 for 中,然后反复执行直到生成器没有 yield 任何内容。
告诉编译器去哪里找,一种方法就是编辑 sys.path
另外一种方法就是使用 PYTHONPATH
环境变量
Python 3.6.1 (default, Apr 23 2017, 12:32:16)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.path)
['', '/home/einverne/.pyenv/versions/3.6.1/lib/python36.zip', '/home/einverne/.pyenv/versions/3.6.1/lib/python3.6', '/home/einverne/.pyenv/versions/3.6.1/lib/python3.6/lib-dynload', '/home/einverne/.pyenv/versions/3.6.1/lib/python3.6/site-packages']
对于不同的操作系统 PYTHONPATH
配置可能有些差异,在类 Unix 系统下
export PYTHONPATH=$PYTHONPATH:~/pypath:~/py1
为组织模块,可以分组为包 package, 为了让 Python 将其作为包对待,必须包含一个 __init__.py
的文件。
假如构造了如下的目录结构
├── douban
│ ├── album.py
│ ├── celebrity.py
│ ├── __init__.py
定义了 douban 目录,那么该文件夹下的 __init__.py
就是 douban 包(模块)的代码,其他两个 album.py
和 celebrity.py
分别是 album 和 celebrity 模块。
如果单纯的引入
import douban
那么 __init__
模块的内容是可用的,但是其他两个模块是不可用的。
import douban.album
那么这个时候 album 模块也是可用的。但这时候只能通过全名 douban.album.
来使用。
from douban import celebrity
这时候 celebrity 模块可用,可以通过短名来使用,比如 celebrity.xxx
模块中定义 __all__
= [] 向外导出可用方法。在别人应用该模块时,打印 __all__
内容就能够清晰看到模块对外提供的方法。
sys 模块能够轻松访问 Python 解释器联系的变量和函数
函数或变量 | 描述 |
---|---|
argv | 命令行参数,包括脚本名字 |
exit([arg]) | 退出,可选参数给定返回值或错误 |
modules | 映射模块名字到载入模块的字典 |
path | 查找模块所在目录的目录名列表 |
platform | 平台 |
stdin | 标准输入流 |
stdout | 标准输出流 |
stderr | 标准错误流 |
os 模块提供了访问操作系统服务的功能。
os.path
子模块包含了检查构造删除目录和文件的函数。
time 模块能够实现,获取当前时间,操作时间和日期,从字符串读取时间以及格式化时间为字符串等等功能。
函数 | 描述 |
---|---|
asctime([tuple]) | 将时间元组转换为字符串 |
localtime([secs]) | 将秒转换为日期元组,本地时间 |
mktime(tuple) | 将时间元组转换为本地时间 |
sleep(secs) | 休眠 |
strptime(string[, format]) | 将字符串解析为时间元组 |
time() | 当前时间,秒 |
random 模块包括返回随机数的函数,可以用于模拟或者任何产生随机数的程序。
f-Strings 在 Python 3.6 以后引入的新特性,新语法,用来输出格式化的文本 (PEP 498)
>>> name = "Eric"
>>> age = 74
>>> f"Hello, {name}. You are {age}."
'Hello, Eric. You are 74.'
Python 以前的格式化输出,总或多或少有些毛病
>>> "Hello, %s. You are %s." % (name, age)
>>> "Hello, {}. You are {}.".format(name, age)
关于输出字符串各种方式的优缺点、性能比较可以参考这篇
Thread 类中有三个方法长得非常像,也特别容易混淆,但是使用起来却非常不同:
public void interrupt() // 无返回值
public boolean isInterrupted() // 有返回值
public static boolean interrupted() // 静态,有返回值
interrupt()
: 中断本线程
myThread.interrupt();// 中断的是调用 interrupt() 方法的线程
阻塞于 wait/join/sleep 的线程,中断状态会被清除掉,同时收到异常 InterruptedException;而其他情况中断状态都被设置,并不一定收到异常。interrupt()
方法其实是通知线程该中断了。线程具体中断还是继续执行,应该由被执行线程自己处理。
具体来说,当一个线程调用 interrupt()
方法时:
- 线程处于阻塞状态(sleep,wait,join 等),线程立即退出被阻塞状态,抛出 InterruptedException 异常
- 线程处于正常活动状态,会将线程中断标志设置为 true,被设置中断标志的线程将继续正常运行
一个线程如果有被中断的需求,在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
Thread thread = new Thread(() -> {
// 若未发生中断,就正常执行任务
while (!Thread.interrupted()) {
// 正常任务代码
}
// 中断处理代码
doSomething();
});
thread.start();
// 一段时间以后
thread.interrupt();
isInterrupted()
: 检测本线程是否已经中断
myThread.isInterrupted();// 判断本线程 myThread 是否中断
如果已经中断,则返回 true,否则 false。中断状态不受该方法的影响。 如果中断调用时线程已经不处于活动状态,则返回 false。
interrupted()
: 检测当前线程是否已经中断
Thread.interrupted();// 判断该语句所在线程是否中断
如果已经中断,则返回 true,否则 false,并清除中断状态。换言之,如果该方法被连续调用两次,第二次必将返回 false,除非在第一次与第二次的瞬间线程再次被中断。如果中断调用时线程已经不处于活动状态,则返回 false。
后两个方法的区别横向比较:
isInterrupted() | interrupted() |
---|---|
实例方法 | 类方法 |
判断本线程 | 判断当前线程 |
仅读取中断状态 | 读取并清除中断状态 |
fdisk 命令用于观察硬盘实体使用情况,可以用来列出机器中所有磁盘的个数,也能列出所有磁盘分区情况,也可对硬盘分区(适用于 2T 以下磁盘,高于 2T 磁盘使用 parted)。
显示所有磁盘的分区详情
fdisk -l
常见的磁盘标示都是 sda, sdb 类似,而分区则是在磁盘标示后面添加数字,比如 sda1, sda2, … , sdb3 等等。
选择进行操作的磁盘
fdisk /dev/sdb
对 U 盘进行格式化,其他设备同理。
# 查看 U 盘挂载点(此例是 /tmp/mnt/sda1)
$ df -h
Filesystem Size Used Available Use% Mounted on
ubi:rootfs_ubifs 77.2M 64.0M 13.2M 83% /
mtd:bootfs 4.4M 3.3M 1.1M 75% /bootfs
mtd:data 8.0M 556.0K 7.5M 7% /data
/dev/mtdblock8 48.0M 9.0M 39.0M 19% /jffs
/dev/sda1 3.5G 51.1M 3.3G 2% /tmp/mnt/sda1
# 卸载 U 盘
$ umount /tmp/mnt/sda1
# 查看 U 盘设备路径(此例是 /dev/sda)
$ fdisk -l
Disk /dev/sda: 3869 MB, 3869544448 bytes
245 heads, 52 sectors/track, 593 cylinders
Units = cylinders of 12740 * 512 = 6522880 bytes
Device Boot Start End Blocks Id System
/dev/sda1 1 593 3777384 83 Linux
# 删除分区、新建分区
$ fdisk /dev/sda
Command (m for help): d # 删除分区
Selected partition 1
Command (m for help): n # 新建分区
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1015, default 1): Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-1015, default 1015): Using default value 1015
Command (m for help): w # 保存分区
The partition table has been altered.
Calling ioctl() to re-read partition table
# 格式化分区为 ext4
mkfs.ext4 /dev/sda1
# 挂载 U 盘
$ mkdir /tmp/mnt/sda1
$ mount -t ext3 /dev/sda1 /tmp/mnt/sda1
默认情况下 MySQL 中存储内容不是大小写敏感的。MySQL 的大小写和建数据库时的排序规则有关。
utf8_bin
则是将字符串中的每一个字符用二进制存储,bin 是 binary case sensitive collation,区分大小写utf8_general_ci
不区分大小写,ci 为 case insensitiveutf8_general_cs
区分大小写,cs 为 case sensitive 缩写在建表时可以通过 BINARY 来区别
比如
CREATE TABLE test
(
name VARCHAR(20),
UNIQUE(name)
);
mysql> INSERT INTO test VALUES('California');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO test VALUES('california');
ERROR 1062 (23000): Duplicate entry 'california' for key 'name'
mysql> INSERT INTO test VALUES('cAlifornia');
ERROR 1062 (23000): Duplicate entry 'cAlifornia' for key 'name'
mysql> INSERT INTO test VALUES('cALifornia');
ERROR 1062 (23000): Duplicate entry 'cALifornia' for key 'name'
mysql> SELECT * FROM test;
+------------+
| name |
+------------+
| California |
+------------+
1 row in set (0.00 sec)
如果需要配置大小写敏感则需要使用 BINARY
mysql> CREATE TABLE test
-> (
-> name varchar(20) BINARY,
-> UNIQUE(name)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> INSERT INTO test VALUES('California');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> INSERT INTO test VALUES('california');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO test VALUES('cAlifornia');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO test VALUES('cALifornia');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> SELECT * FROM test;
+------------+
| name |
+------------+
| California |
| cALifornia |
| cAlifornia |
| california |
+------------+
4 rows in set (0.00 sec)
当然在建库,或者建表时已经排序规则之后就要按照之前的约定,如果没有约定,按照默认则需要自己指定。
强制让 where 语句中区分大小写需要在 where 后添加 binary
select * from table where binary name='Abc'
需要修改配置
/etc/mysql/my.cnf
在 [mysqld] 配置下面:
lower_case_table_names = 1
然后需要重新加载 mysql 配置或者重启 MySQL 服务。
当前正在运行的 shell 路径被保存在 $0
环境变量中,可以使用如下方式查看
echo $0
根据不同系统的实现,输出可能会是当前正在运行的 shell,或者是当前运行的 shell 的路径。
prompt:~$ echo $0
/bin/bash
prompt:~$ sh
sh-4.0$ echo $0
sh
sh-4.0$ exit
exit
prompt:~$ /bin/sh
sh-4.0$ echo $0
/bin/sh
sh-4.0$
$SHELL
变量保存了用户偏好的 shell,而不是当前正在运行的 shell。
更多关于 shell 的默认特殊变量,可以查看之前的总结。
推荐好用的网站系列之生成邮件签名 htmlsig 。想要一个漂亮的邮件签名,又不想自己写 html,最好的方法就是找一个模板然后自己填写内容。这个网站就是这样的。
官网地址:https://htmlsig.com/
样式1
样式2
样式3
样式4
当然我本人最喜欢样式2.
如果稍微懂一点 html 知识,将模板下载下来然后自己手动修改倒也是不错的选择。
生成自己的模板之后,Gmail 和 Inbox 都可以使用复制粘贴的方式将签名添加进去。
因项目需求,需要使用 C++ 解析 JSON。
第一种方法,使用 RapidJSON 可以方便的用来生成或者解析 JSON。
项目地址:https://github.com/miloyip/rapidjson
RapidJSON 是只有头文件的 C++ 库。使用时只需要把 include/rapidjson
复制到项目目录中即可。
类似如下的JSON,其中包括Object,包括Array,掌握解析该JSON,基本 RapidJSON 解析可掌握:
{
"ret": "101",
"error": [
{
"errortype": "A0001",
"errorstroke": {
"0": "0.2",
"1": "0.3"
}
},
{
"errortype": "A0021",
"errorstroke": {
"0": "0.2",
"1": "0.3"
}
}
]
}
代码如下:
#include <iostream>
#include <vector>
#include <string>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
using namespace std;
int main() {
string ret =
"{\"ret\":\"101\",\"error\":[{\"errortype\":\"A0001\",\"errorstroke\":{\"0\":\"0.2\",\"1\":\"0.3\"}},{\"errortype\":\"A0021\",\"errorstroke\":{\"0\":\"0.2\",\"1\":\"0.3\"}}]}";
rapidjson::Document doc;
doc.Parse<kParseDefaultFlags>(ret.c_str());
if (doc.HasMember("ret")) {
string retjson = doc["ret"].GetString();
for (unsigned i = 0; i < retjson.length(); ++i) {
cout << retjson.at(i) << " ";
}
}
cout << endl;
if (doc.HasMember("error")) {
const Value & errorjson = doc["error"];
for (SizeType i = 0; i < errorjson.Size(); ++i) {
// 或者这里可以换用一种遍历使用 Value::ConstValueIterator
// http://rapidjson.org/md_doc_tutorial.html#QueryArray
if (errorjson[i].HasMember("errortype")) {
string errortype = errorjson[i]["errortype"].GetString();
cout << "errortype: " << errortype << endl;
}
if (errorjson[i].HasMember("errorstroke")) {
const Value & errorstroke = errorjson[i]["errorstroke"];
cout << "errorstroke" << endl;
for (Value::ConstMemberIterator iter = errorstroke.MemberBegin();iter != errorstroke.MemberEnd(); ++iter) {
cout << iter->name.GetString() << ": " << iter->value.GetString() << endl;
}
}
}
}
return 0;
}
关于 RapidJSON 的更多内容可以参考官网。
使用 boost 库中的 property_tree
解析如下:
/*
first config your project to include /usr/local/include
second link lib /usr/local/lib
*/
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
#include <string>
using namespace boost::property_tree;
int main(int argc, const char * argv[]) {
std::string str_json = "{\"ret\":\"101\",\"error\":[{\"errortype\":\"A0001\",\"errorstroke\":{\"0\":\"0.2\",\"1\":\"0.3\"}},{\"errortype\":\"A0021\",\"errorstroke\":{\"0\":\"0.2\",\"1\":\"0.3\"}}]}";
ptree pt; //define property_tree object
std::stringstream ss(str_json);
try {
read_json(ss, pt); //parse json
} catch (ptree_error & e) {
return 1;
}
std::cout << pt.get<std::string>("ret") << std::endl;
ptree errortype = pt.get_child("error"); // get_child to get errors
// first way
for (boost::property_tree::ptree::iterator it = errortype.begin(); it != errortype.end(); ++it) {
std::cout << it->first;
std::cout << it->second.get<std::string>("errortype") << std::endl;
ptree errorstroke = it->second.get_child("errorstroke");
for (ptree::iterator iter = errorstroke.begin(); iter != errorstroke.end(); ++iter) {
std::string key = iter->first;
std::cout << iter->first << std::endl;
std::cout << iter->second.data() << std::endl;
}
}
// second way: using boost foreach feature
// BOOST_FOREACH(ptree::value_type &v, errortype){
// ptree& childparse = v.second;
// std::cout << childparse.get<std::string>("errortype") << std::endl;
// ptree errorstroke = childparse.get_child("errorstroke");
// BOOST_FOREACH(ptree::value_type& w, errorstroke){
// std::cout << w.first << std::endl;
// std::cout << w.second.data() << std::endl;
// }
// }
/*
101
A0001
0
0.2
1
0.3
A0021
0
0.2
1
0.3
*/
return 0;
}
早上去的时候一大群熊孩子在外面排队吓得我差点想要放弃,其实后来才发现到的时候没有开馆,排了一会儿队就进去了,还是很快的。其实这个地方还只适合亲子去游玩,如果真的高中都毕业了,真的看到没有意思了,涉及到的一些物理,化学小道具都是课本上曾经存在过的实验。如果有机会未来带小孩来玩一玩还是挺不错的。
进门就能看到这只巨大的恐龙化石。
去的时候直接从顶层往下逛的,馆中走道还有不少奥运的雕塑。
在上几层物理展馆中还是有不少有趣的玩意儿的,没拍多少照片,让我驻足的有如下的傅科摆,曾经屋里课本上学单摆的时候有看到过。当然傅科摆也间接地证明了地球的自转。物理那块区域的电生磁,磁生电,光等等区都是挺有趣的。
古代计时工具—-日晷。
最后走的时候在一层见到了很多中国古代天文,地理,木工等等的仪器和小工具,给我印象深刻的就是这个鲁班锁,用6块切割好的木块能够拼接成如图的形状。
Google Photos 官网:http://googlephotos.blogspot.com/
Picasa Resources : https://sites.google.com/site/picasaresources/Home/Picasa-FAQ
这个网站整理了 Google Picasa Help Forum 中的很多问题,也解决了困惑我很久的问题,比如 新 Google Photos 中相册的排序问题,比如 Google Photos 中分享出去照片自定义大小的问题,比如 Picasa Web Album 关闭之后的问题。
总之有关从 Picasa 平稳迁移到 Google Photos 的很多问题基本都能找到解决方案。
还有一个 Top Contributor 自己的网站 : http://picasageeks.com/ 也是很棒,总结了各种经验。
虽然 Google 关闭 Picasa 来看,对长期使用 Picasa 的老用户来说是件悲痛的事情,就像当时 Google 关闭 Google Reader 一样。但是多少年过去了,可能新用户根本不知道曾经有一个 Google Reader 存在过。从公司的角度看 关闭 Picasa 一心 Google Photos 当然也无口厚非,集中一心把一款产品打造好。只是从 Picasa 到 Google Photos 走得太快,以至于 Picasa Web Album 很多很实际的功能 Google Photos 一个都没有。而 Google Photos 一直在宣传的功能 Picasa 却很早就就拥有。这里本不想多说却还是依然写了这么多。
转到 Google Photos 本身这个产品,如果是新用户并且是移动设备使用时长较多的话,它本身是一款非常棒的产品: 1. 全备份(日期排序) 2. 简单修图工具 3. 相册以及好用的分享工具。 单就这三点已经完全满足一个相册应该具备的功能了。而反过来真是因为在移动设备上的简单,以及没有对老用户的照顾,Google Photos 中的时间线,相册管理远远不及 Picasa。但是细想原本这两款产品针对的用户就是不一样的。
Picasa 这一款产品是一款云端相册,用来提供给用户分享照片,因此重在 Web ,以及相册管理
Google Photos 私人相册,云端相册,重在移动,重在备份,虽然也有分享功能却很弱。上面 Picasa Geeks 网站上有篇文章写得好,列举了 Google Photos 没有的功能。在 Web 上,缺乏排序功能,分享设置只有 Private 和 Public 两个选项,而 Picasa Web Album 有 Public,Limited(Anyone with link), Limited(Listed People), Private 四个选项,而这4个选项和 Google Drive 文件分享类似。希望 Google Photos 之后会把这些功能都添加上吧。
总之事情已经这样,结局无法改变,现在提前去适应一下 Google Photos 也好,不至于到时候慌乱。我关注的事情如下:
在之前的文章中我都是使用的 Picasa Web Album 分享出来的图片链接,Picasa 提供的免费无限图床真是赞到家,不仅没有流量限制,还能自定义大小。
比如下面两张照片,通过修改 s144-Ic42
参数就能够变换图片的大小,当然具体参数可以从这里 查到。最常用的可能就是改 s0
获得原图了吧。
在 Picasa 关闭之后获取直链成为一个问题,我在 StackOverflow 上面的提问也没有任何实质性的解决。不过在后来的使用过程中发现,将照片分享到 Google+ ,这时 Google 会产生一个直接的图片 URL,点击看图片,并右击复制图片链接,就可以获取和 Picasa 分享类似的链接。
这要从很久很久以前说起,我原先的照片管理一直依照相册来管理,比如今天可能拍了很多照片,我会以 日期-活动 ,例如 160311-Event 来命令相册然后通过合适的分享途径分享出去,如果我想使用某张照片到博客中,获取直链并添加到博客配图即可。可是在 Google+ Photos 时代,Google 就彻底搞乱了我的相册管理方法。Picasa 中被莫名其妙的添加了很多的相册。自此之后相册管理体系彻底崩溃,没有了清晰的相册管理,现在我已经不管相册了,按照 Google Photos 给我的时间流排布照片,适当的时候将图片添加到相册中。其他时候基本上放任 Google Photos 自己备份。
在 Google Photos App 中即使我想分享一个相册我首选的也是讲照片内容传到 Google+ ,并不会优先使用 Google Photos 的分享功能,所以至今为止,我的 Share 相册中也只有当时测试使用过的一个相册。
测试帖如上
可能让我唯一开心一点的就是 Picasa 到 Google+ Photos 到 Google Photos 的容量变成了无限大。其实听到这个消息的时候,我的相册容量已经到到了10G,当时正愁怎么办呢。随之后面的改变就已经很吸引人了,从 Google+ 时代的 2048*2048
像素以下不算空间,到现在 Google Photos 的16MP 下不算空间,几乎已经是无限容量的节奏了,我手机最高像素也没这么大。。
最后的吐槽,真的不想管这个了,改来改去太累了。
排序算法复习,插入排序,选择排序,冒泡排序,希尔排序,[[归并排序]],堆排序,快排。
关于排序算法的 stable 稳定性,排序保存原始数据顺序则稳定,否则不稳定。
关于原址排序,算法需要额外的空间计算或者保存数据, in-place sorting ,归并排序为非原址排序 not-in-place sorting。
关于时间复杂度,归并排序,堆排序,快排有相对较快的速度 O(n*log(n))
排序前后两个相等的数的相对位置不变。
有一些排序算法天然是稳定的,比如 Insertion Sort, Merge Sort, Bubble Sort。
加入有一组英文名,包括 First Name 和 Last Name,要求先按照 Last Name(姓) 排序,然后按照 First Name (名) 排序,这个时候可以先排序 First Name (稳定或非稳定),然后按照稳定的排序算法排序 Last Name,这样就可以保证排序完成之后,就是最终的结果。
每次取一个元素插入正确的位置,适合少量元素。对于未排序的数据,从已排序的序列中从后向前扫描,找到相应的位置插入,实现上通常使用 in-place 排序,只需要使用额外 O(1) 空间,但是因为插入正确的位置之后,需要反复移动已经排序的序列,为新元素提供插入空间,因而比较费时。
一般来说,插入排序都采用 in-place 在数组上实现。具体算法描述如下:
for i = 2:n,
for (k = i; k > 1 and a[k] < a[k-1]; k--)
swap a[k,k-1]
→ invariant: a[1..i] is sorted
end
Java 版本:
static void insert_sort(int[] array) {
for(int i = 1; i < array.length; ++i) {
int cur = array[i];
for(int j = i - 1; j >= 0 && array[j] > cur; j--) {
array[j + 1] = array[j];
array[j] = cur;
}
}
}
list = [4,6,2,5,1,3,0,4,8,1,5,3,6]
# 升序
# 从第二个元素开始,每次循环将前 i 个元素排序
for i in range(1,len(list)):
value = list[i]
j = i-1
# 将第 i 个元素的位置腾出
while j >= 0 and list[j]>value:
list[j+1] = list[j]
j=j-1
# 在排完序的 list[0...i] 中将值插入合适位置
list[j+1]=value
# 降序
list = [4,6,2,5,1,3,0,4,8,1,5,3,6]
for i in range(len(list)-1, -1, -1):
value = list[i]
j=i+1
while j<len(list) and value < list[j]:
list[j-1] = list[j]
j=j+1
list[j-1]=value
print(list)
每次选取数组中最小(或者最大)的元素,并将其与未排序数组中首元素交换,依次进行。
选择排序拥有最小的交换次数,适合交换元素开销比较大的情况。选择排序其他情况都比较低效。
for i = 1:n,
k = i
for j = i+1:n, if a[j] < a[k], k = j
→ invariant: a[k] smallest of a[i..n]
swap a[i,k]
→ invariant: a[1..i] in final position
end
list = [4,6,2,5,1,3,0,4,8,1,5,3,6]
for i in range(0,len(list)):
k = i
for k in range(i+1, len(list)):
# 没有完全按照定义写,不过这样交换的开销更大。
if list[k] < list[i]:
list[i], list[k] = list[k], list[i]
print(list)
Java 版:
static void selection_sort(int[] array) {
if(array.length <= 1) return;
for(int i = 0; i < array.length; i++) {
int smallest = i;
for(int j = i + 1; j < array.length; j++) {
if (array[j] < array[smallest]) {
smallest = j;
}
}
int temp = array[i];
array[i] = array[smallest];
array[smallest] = temp;
}
}
[[冒泡排序]] 反复交换相邻未按次序排列的元素,一次循环之后最大的元素到数组最后。
for i = 1:n,
swapped = false
for j = n:i+1,
if a[j] < a[j-1],
swap a[j,j-1]
swapped = true
→ invariant: a[1..i] in final position
break if not swapped
end
def bubble_sort_1(list):
for i in range(0, len(list)):
for j in range(1, len(list)-i):
if list[j-1] > list[j]:
list[j-1], list[j] = list[j], list[j-1]
def bubble_sort_2(list):
for i in range(0, len(list)):
swap = False
for j in range(len(list)-1, i, -1):
if list[j-1] > list[j]:
list[j-1], list[j] = list[j], list[j-1]
swap = True
if swap is False:
break
相较于第一种直接冒泡,设定标志优化冒泡。
Java 版
static void bubble_sort(int[] arr) {
int i, j, temp, len = arr.length;
for (i = 0; i < len - 1; i++)
for (j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
分组插入排序,将数组拆分成若干子序列,由增量决定,分别进行直接插入排序,然后缩减增量,减少子序列,最后对全体元素进行插入排序。插入排序在基本有序的情况下效率最高。
h = 1
while h < n, h = 3*h + 1
while h > 0,
h = h / 3
for k = 1:h, insertion sort a[k:h:n]
→ invariant: each h-sub-array is sorted
end
list = [4,6,2,5,1,3,0,4,8,1,5,3,6]
def insertion_sort(k,h,n):
"""
:param k: group count
:param h: step length
:param n: total
:return:
"""
for i in range(k+h, n, h):
value = list[i]
j = i-h
while j >= 0 and list[j]>value:
list[j+h] = list[j]
j=j-h
list[j+h]=value
h = 1 # step length
while h < len(list):
h = 3*h +1
while h > 0:
h = int(h / 3)
for k in range(0, h): # devide into k groups
insertion_sort(k, h, len(list))
print(list)
[[归并排序]] 是一个典型的分治算法,将数组分成两个子数组,在子数组中继续拆分,当小组只有一个数据时可认为有序,之后合并,所以重点就到了合并有序数组。
# split in half
m = n / 2
# recursive sorts
sort a[1..m]
sort a[m+1..n]
# merge sorted sub-arrays using temp array
b = copy of a[1..m]
i = 1, j = m+1, k = 1
while i <= m and j <= n,
a[k++] = (a[j] < b[i]) ? a[j++] : b[i++]
→ invariant: a[1..k] in final position
while i <= m,
a[k++] = b[i++]
→ invariant: a[1..k] in final position
From wiki
from collections import deque
def merge_sort(lst):
if len(lst) <= 1:
return lst
def merge(left, right):
merged,left,right = deque(),deque(left),deque(right)
while left and right:
merged.append(left.popleft() if left[0] <= right[0] else right.popleft()) # deque popleft is also O(1)
merged.extend(right if right else left)
return list(merged)
middle = int(len(lst) // 2)
left = merge_sort(lst[:middle])
right = merge_sort(lst[middle:])
return merge(left, right)
利用最大堆的性质,堆性质,子结点的值小于父节点的值。每次将根节点最大值取出,剩下元素进行最大堆调整,依次进行。
# heapify
for i = n/2:1, sink(a,i,n)
→ invariant: a[1,n] in heap order
# sortdown
for i = 1:n,
swap a[1,n-i+1]
sink(a,1,n-i)
→ invariant: a[n-i+1,n] in final position
end
# sink from i in a[1..n]
function sink(a,i,n):
# {lc,rc,mc} = {left,right,max} child index
lc = 2*i
if lc > n, return # no children
rc = lc + 1
mc = (rc > n) ? lc : (a[lc] > a[rc]) ? lc : rc
if a[i] >= a[mc], return # heap ordered
swap a[i,mc]
sink(a,mc,n)
def max_heapify(lst, i):
"""
下标为 i 的根节点调整为最大堆
:param lst:
:param i:
:return:
"""
l = 2 * i + 1
r = 2 * (i + 1)
if l < len(lst) and lst[l] > lst[i]:
largest = l
else:
largest = i
if r < len(lst) and lst[r] > lst[largest]:
largest = r
if largest != i:
lst[i], lst[largest] = lst[largest], lst[i]
max_heapify(lst, largest)
def build_max_heap(lst):
"""
建立最大堆
"""
for i in range((len(lst)-1)/2, 0, -1):
max_heapify(lst, i)
def heap_sort(lst):
build_max_heap(lst)
ret = []
for i in range(len(lst)-1, -1, -1):
ret.append(lst[0])
lst.remove(lst[0])
max_heapify(lst, 0)
return ret
L = [16, 4, 10, 14, 7, 9, 3, 2, 8, 1]
R = heap_sort(L)
print(R)
从数列中挑出元素,将比挑出元素小的摆放到前面,大的放到后面,分区操作。然后递归地将小于挑出值的子数列和大于的子数列排序。
# choose pivot
swap a[1,rand(1,n)]
# 2-way partition
k = 1
for i = 2:n, if a[i] < a[1], swap a[++k,i]
swap a[1,k]
→ invariant: a[1..k-1] < a[k] <= a[k+1..n]
# recursive sorts
sort a[1..k-1]
sort a[k+1,n]
list_demo = [2,8,7,1,3,5,6,4]
def partition(lst, p, r):
"""
划分
:param lst: 待排序数组
:param p: 起始下标,子数组第一个元素
:param r: 终止下标,子数组最后一个元素 r < len(lst)
:return: 划分结果下标
"""
if r >= len(lst) or p < 0:
return
x = lst[r]
i = p - 1
for j in range(p, r):
if lst[j] <= x:
i += 1
lst[i], lst[j] = lst[j], lst[i]
lst[i+1], lst[r] = lst[r], lst[i+1]
return i + 1
def quick_sort(lst, p, r):
if p < r:
q = partition(lst, p, r)
quick_sort(lst, p, q-1)
quick_sort(lst, q+1, r)
quick_sort(list_demo, 0, len(list_demo)-1)
print(list_demo)
箱排序也称桶排序 (Bucket Sort),其基本思想是:设置若干个箱子,依次扫描待排序的记录 R[0],R[1],…,R[n-1],把关键字等于 k 的记录全都装入到第 k 个箱子里(分配),然后按序号依次将各非空的箱子首尾连接起来(收集)。对于有限取值范围的数组来说非常有效,时间复杂度可以可达 O(n). 例如给人年龄排序,人的年龄只能在 0~100 多之间,不可能有人超过 200, 适用桶排序。
箱排序中,箱子的个数取决于关键字的取值范围。 若 R[0..n-1] 中关键字的取值范围是 0 到 m-1 的整数,则必须设置 m 个箱子。因此箱排序要求关键字的类型是有限类型,否则可能要无限个箱子。
箱子的类型应设计成链表为宜 一般情况下每个箱子中存放多少个关键字相同的记录是无法预料的,故箱子的类型应设计成链表为宜。
为保证排序是稳定的,分配过程中装箱及收集过程中的连接必须按先进先出原则进行。
桶排序的平均时间复杂度是线性的,O(n), 但是最坏的情况可能是 O(n^2)
基数排序是非比较排序算法,算法的时间复杂度是 O(n). 相比于快速排序的 O(nlgn), 从表面上看具有不小的优势。但事实上可能有些出入,因为基数排序的 n 可能具有比较大的系数 K. 因此在具体的应用中,应首先对这个排序函数的效率进行评估。
基数排序的主要思路是,将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零. 然后,从最低位开始,依次进行一次稳定排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
这个算法的难度在于分离数位,将分离出的数位代表原元素的代表,用作计数排序。但是分离数位不能脱离原来的数字,计数排序的结果,还是要移动原元素。
注意计数排序的元素数值与位置的联系,引申到基数排序的从元素得到中间值然后与位置的联系。
通常也被叫做秩排序 (Rank Sort) ,算法基本思想是:对每一个要排序的元素,统计小于它的所有元素的个数,从而得到该元素在整个序列中的位置,时间复杂度为 O(n^2)