python入门
这篇文章算是入门的索引。其实,这里可能会放所有的比较杂的知识点。
一些比较优质的资源。
资源
- https://docs.python.org/zh-cn/3/library/threading.html
- 菜鸟教程python
- 如何在python中调用C语言代码
- python3-cookbook 貌似也不错的图书
零散知识点
乱码问题
python2.7乱码问题。下面的代码是写在greenplum中的测试脚本。最终如下:
CREATE OR REPLACE FUNCTION scc_hello(addr text) RETURNS text AS $$
#coding=utf-8
return '#'.join([x.encode('utf-8') for x in addr.decode('utf-8')])
$$LANGUAGE plpythonu;
在遍历中文字符的时候,如果不先进行decode,再decode,则会各种乱码。(具体参见 乱码.txt)
>>> addr='您好世界'
>>> print(addr)
您好世界
>>> '#'.join([x.encode('utf-8') for x in addr.decode('utf-8')]
KeyboardInterrupt
>>> '#'.join([x.encode('utf-8') for x in addr.decode('utf-8')])
'\xe6\x82\xa8#\xe5\xa5\xbd#\xe4\xb8\x96#\xe7\x95\x8c'
>>> print('#'.join([x.encode('utf-8') for x in addr.decode('utf-8')]))
您#好#世#界
为什么要先decode,再encode呢?
先记住这样,u.encode,str.decode,而str函数,。。。
对中文的遍历,在python2.7的时候,默认按ascii码来取,所以呢,中文的字,会变成两2,无法正常的遍里,故,先转换为unicode编码(unicode中的一个就代表完整的一个字),然后运算完,再转换为string。即可。同理,[:]切片应该也遵循相同的道理。
上面仅仅是针对2.7的,而且也废弃了。
对于3.6及以上的版本。见到过b'somechar'的,只接使用str()函数来处理。
查看python模块的位置
-- 方式1
pydoc modules
-- 方式2,先进入交互模式,然后再输入命令
pydoc
help("modules")
assert
有的时候,我们希望出错了,不太想做具体的判断,直接exit,则可以用assert关键字。
def save_csv(filename,obj_list):
assert obj_list is not None and len(obj_list) > 0
raise
手动抛出异常,如果上层调用处,没有做错误捕捉,则可能导致程序退出。
raise error('save_csv : 数据有误')
pprint
第一次知道,还有这样的函数。2021-10-29 16:57
from pprint import pprint,pformat
# 格式化输出的内容
pprint(dic)
# 输出的内容,形似json,正好也是,python中定义,定义字段的代码。
print(eval(pformat(res.data.rows[0])))
全局变量
如下代码,必须要申明cookies 为全局的cookies,python判断一个变量是否是新的变量,貌似的机制是:
如果一个变量,上来就用,没有定义过,那么认为,这个变量是全局的。
而相反,如果发现上来就对一个变量赋值,则认为,这是在申明一个新的变量,导致,函数作用域的这个新变量覆盖全局的同名变量。
这有点神奇,js呢,少var则认为是全局。python呢,看你变量是不是上来就用,还是上来就赋值来区分。php呢,则全认为是局部变量。好吧,原来如此。
def get_cached_cookie():
global cookies
if os.path.isfile(COOKIES_FILE):
with open(COOKIES_FILE,'r') as fi:
cookies = json.load(fi)
输入处理
python2的时候,使用raw_input,以下为python3代码:
num = input("输入你的工号:")
passwd = input("输入你的密码:")
字符串作为函数来调用
在php中,可以直接使用字符串来调用函数,但是python中如何来用呢?参见文章
函数调用
# 1、eavl方式
eavl('func')()
# 2、locals()和globals() 是python的两个内置函数,以字典类型返回当前位置的全部局部和全部全局变量.
locals()['func']()
对象方法调用
# 1、getattr()
# getattr() 是 python 的内建函数,getattr(object,name) 就相当于 object.name,但是这里 name 可以为变量。返回 foo 模块的 bar 方法
f = getattr(foo_instance, 'do_' + opname)
f()
# 2、methodcaller
f = Foo()
from operator import methodcaller
methodcaller('do_foo')(f)
# 标准库operator下的methodcaller函数
生成器,省略括号
发现,貌似有括号的地方,写生成器,可以直接省略。如下。
from random import choice
data = ''.join(choice('ABCD') for i in range(10)) # join 的括号 被当作了生成器的括号
print(data)
def mytest(*text):
print(text)
mytest(choice('ABCD') for i in range(10))
#print('hello world') for i in range(10) # 没有括号,就不能省略了
(print('hello world') for i in range(10))
字典生成器
不必说,常用的数组生成器,对象也有生成器。但是,注意一下两种格式的区别。
# 方式1: key value都是从迭代中直接获取
demo = {'age':1,"sex":2}
d2 = {k: v for (k, v) in demo.items()}
rows = [
{'id':1,'name':"zhangsan1"},
{'id':2,'name':"zhangsan2"},
{'id':3,'name':"zhangsan3"},
{'id':4,'name':"zhangsan4"},
]
# 方式2: 从迭代中,只拿一个变量,其他都是运算所得。
{item['id']:item['name'] for item in rows }
# 甚至,直接遍历一个数组
{"key%d"%i:i for i in range(10)}
只从迭代中只拿一个变量,这种方式更加常用。推荐。
数组的加乘法运算
tmp = [i for i in range(10)]
tmp += ['']*4 # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, '', '', '', '']
# 但是没有减 除运算
# 以下两种,都是达到相同的目的。
[i for i in range(10)] + [33]
[i for i in range(10)].append(33) # 但是没有返回值。
如果求list的并集等,如何计算呢?set有此概念,故先转换为set。
对比较运算符号也做了重载。
[10] > [2,1]
集合运算
list的集合运算需要借助于set来完成。示例如下:
a = [2, 3, 4, 5]
b = [2, 5, 8]
# 交集
print([val for val in a if val in b])
print (list(set(a).intersection(set(b))))
# 并集
print(a + [var for var in b if var not in a ])
print (list(set(a).union(set(b))))
# 差集
print([var for var in b if var not in a ])
print (list(set(b).difference(set(a)))) # b中有而a中没有的
序列解包
常用,其他语言,js/php都有对应的概念。
另:参见map/filter本质
# other前面的 * 用来匹配剩余所有的参数,可以有0+个元素,封装为一个数组
first_number,*other = data['job_number'].split(',')
# playload = {} 字典 ,因为format接受的参数是 format(key1=value1,)
'{name}...'.format(**payload)
# 数组也是一样,一个 * 号。
params = [a,b,c]
test(a,b,c)
test(*params)
另一种用法,交换元素的值,或者变量批量初始化。
a = 1
b = 2
a,b = b,a
print(a)
print(b)
引入模块
https://zhuanlan.zhihu.com/p/64893308
- 导入同级目录下的文件。
import demo # 同级目录的文件,不用加py扩展名,类似于 nodejs
- 其他位置的Python文件,如果想直接引入,则先执行下面,再引入
sys.path.append("../path/to") #
- 引入子目录的文件,先增加空的
__init__.py文件,整体作为一个模块,然后再引入。
# 假入 module跟当前Python文件在同一路径, 欲引入 module/demo.py
# 然后增加 __init__.py 把它当作一个模块来处理。
from module import demo
- 其他,不过是以下的组合拳,先把模块或python文件,加到系统搜索路径内,然后再引入。
sys.path.append("../path/to") #
import demo
from module import demo
数组、元组比较
这个有点神奇,其他语言从来没有这种比较。以下代码摘自requests/__init__.py
# 元组比较
assert (3, 0, 2) <= (major, minor, patch) < (5, 0, 0)
# 数组也能比较
cryptography_version = list(map(int, cryptography_version.split('.')))
cryptography_version < [1, 3, 4]:
运算符重载
python是允许对运算符重载。如数组、元组可以使用逻辑比较符号,数组可以加、乘运算等。pandas里面的中括号语法,貌似也是运算符号重载。
运算符号,可以简单的理解是,函数、方法的别名,但看起来更间接、直观,本质上,还是函数、方法的调用。
参考链接:https://zhuanlan.zhihu.com/p/162931696
zip
zip生成一个只能迭代一次的迭代器,一旦你迭代了它,它就耗尽了。(py3是,py2好像可以反复使用)
主要作用,相当于将数据库的多列(多个数组),合并成一个表的感觉(一个数组(每个数组有多列))。这样,可以遍历每一行。
另:参见map/filter本质
numbers = [1,2,3]
letters = ['a','b','c']
myzip = zip(numbers,letters)
print(myzip)
print(type(myzip))
# 转换成list 并保存返回值,就能多次循环使用了
# print(list(myzip))
# for i in list(myzip):
# print(i)
# for k,v in list(myzip):
# print('%s\t%s'%(k,v))
# for k,v in myzip:
# print('%s\t%s'%(k,v))
# print(list(zip(*myzip)))
# 兜兜转转,又回到原来的样子
a,b = zip(*myzip)
print(a)
print(b)
print('done')
map/filter本质
def _map(fn, *iterables):
return [fn(*i) for i in zip(*iterables)] # 序列解包
def _filter(fn, iterable):
return [i for i in iterable if fn(i)]
print(_map(lambda x,y: x +y ,[1,3,5],[2,4,6])) # [3, 7, 11]
print(list(map(lambda x,y: x +y ,[1,3,5],[2,4,6]))) # [3, 7, 11]
print(_filter(lambda x:x%2==0 ,range(10))) # [0, 2, 4, 6, 8]
print(list(filter(lambda x:x%2==0 ,range(10)))) # [0, 2, 4, 6, 8]
环境
虚拟环境
python3.3以上好像自带venv命令。
docker虚拟环境
docker有很好的隔离特性,也可以作为一种虚拟环境来使用。比如:
FROM 10.131.9.12:5000/python
WORKDIR /opt/py
ADD requirements.txt .
RUN pip3 install -r requirements.txt
# 增加自己的代码或目录,不增加的话,可以直接使用映射
ADD *.py ./
将python脚本跑在定时任务中
# echo "0 0 * * * $PWD/run.sh >> $PWD/run.log 2>&1"
docker run --rm -t ldap2mysql:0.1 python3 ldap_save.py
# 注意不要加 -i 选项。
# 直接做脚本运行时,存到 /usr/local/bin/mypython 然后执行mypython来运行
docker run --rm -it -v $PWD:/opt/py -w /opt/py ldap2mysql:0.1 python3 $@
定时任务时,不要加 -i选项,因为会输出:
the input device is not a TTY
但是,我看的文章说,去掉-t,即不需要分配伪终端。
ipython
pip3 install ipython
requests.request? # 查看说明
requests.request?? # 查看源代码 + 说明
%time sleep(1)
%lsmagic # 列出魔法变量
!pip3 install bs4 # 执行系统命令
jupyter
参考链接 https://zhuanlan.zhihu.com/p/33105153/
底层也依赖了ipython,所以部分操作跟ipython很像。不再详述。
网页版本的。可以使用docker环境来使用。也可以在本地环境安装。本地安装如下:
# 安装
pip3 install jupyter
# 使用 ,默认目录为当前cmd目录
jupyter notebook
jupter notebook --port 9999 --no-browser
使用说明
jupyter的用途,其实是在写测试代码,因为,每次只运行一个窗格,而之前的状态,都会保存,比如,一些变量等等。但是,我试过,用jupter来写代码,反而有点鸡肋,为啥呢?因为,我花了1个小时左右,把所有的代码调通,然后,又花了1个小时,将前面的半成品,调整,成为一个程序。整体,非常的浪费时间。
用法:用来测试代码。写的所有的代码,尽量都封装成一个个函数。这样,方便,后期整体封装成一个程序。(或者,同步的,写一个脚本,写一个函数,集成测试一下?)
jupyterlab
高级版的jupyter,功能多一点。
pip install jupyterlab
# 运行
jupyter-lab
更多操作,参见jupyter安装
升级pip
sudo pip3 install fabric
error like this:
Command "python setup.py egg_info"
sudo pip3 install --upgrade setuptools
sudo python3 -m pip install --upgrade pip
sudo pip3 install fabric
python多环境
在windows下安装了anaconda3,故按以下方式安装python多环境。
# 查看可安装的版本
conda search -f python
# 查询的虚拟环境
conda info --envs 或者conda env list
# 创建
conda create -n py310 python=3.10.4
# 激活环境
conda activate py310
# 退出环境
conda deactivate
# 移除虚拟环境
conda remove -n yourenvname -all
另一种方式,在Linux可以直接采用Docker的方式,创建python多环境版本。
或者安装两个版本,注意一下命名。可参考本文章
代码片段
字典生成器
亮点:字典生成,外层迭代table,内存迭代s。
def main():
html = get_page_content()
soup = BeautifulSoup(html, 'lxml')
table = soup.find('table', id='proxylisttable')
rows = table.find_all('tr')[1:-1] # remove first and last row
s = ('ip', 'port', 'code', 'contry', 'anonymity', 'google', 'https', 'lastchecked')
for tr in rows:
td = tr.find_all('td')
yield {key: td[i].string for i, key in enumerate(s)}
#print(list(main()))