微信公众号判断用户是否已关注php代码解析

scutephp 发表了文章 • 0 个评论 • 371 次浏览 • 2017-01-17 21:24 • 来自相关话题

现在的活动,很多都引导用户关注公众号,才能参与到活动当中,那如何才能判断用户关注了公众号呢? 本文就为大家提供php代码,解决问题。

官方接口说明
获取用户基本信息(包括UnionID机制) 

Http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.HTML

1、只要有基础的access_token和用户openid就可以判断用户是否关注该公众号
2、利用的接口url为:https://api.weixin.qq.com/cgi-bin/user/info?access_token=$token&openid=$openid
3、判断接口返回的字段subscribe是否为1.【1关注,0未关注】

注:
1、判断用户登录的方式为静默授权,用户无感知,从而得到用户的openid;
2、判断用户登录,需要微信认证服务号的支持,订阅号不行; 

下面是代码案例
 < ? php

$access_token = $this - > _getAccessToken();
$subscribe_msg = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token='.$access_token.'&openid='.$_<a data-cke-saved-href="http://www.kubiji.cn/juhe_listing-idPHPSESSION.html" href="http://www.kubiji.cn/juhe_listing-idPHPSESSION.html">session</a>['wecha_id'];
$subscribe = <a data-cke-saved-href="http://www.kubiji.cn/juhe_listing-idPHPJSON.html" href="http://www.kubiji.cn/juhe_listing-idPHPJSON.html">json</a>_decode($this - > <a data-cke-saved-href="http://www.kubiji.cn/juhe_listing-idPHPCURL.html" href="http://www.kubiji.cn/juhe_listing-idPHPCURL.html">curl</a>Get($subscribe_msg));
$zyxx = $subscribe - > subscribe;

if ($zyxx !== 1) {
echo'未关注!';
}
private function _getAccessToken() {
$where = array('token' = > $this - > token);
$this - > thisWxUser = M('Wxuser') - > where($where) - > find();
$url_get = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this - > thisWxUser['appid'].'&secret='.$this - > thisWxUser['appsecret'];
$json = json_decode($this - > curlGet($url_get));
if (!$json - > errmsg) {
} else {
$this - > error('获取access_token发生错误:错误代码'.$json - > errcode.',微信返回错误信息:'.$json - > errmsg);
}
return $json - > access_token;
}
? >

 
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持酷笔记。

原文链接:http://www.kubiji.cn/topic-id3162.html 查看全部
现在的活动,很多都引导用户关注公众号,才能参与到活动当中,那如何才能判断用户关注了公众号呢? 本文就为大家提供php代码,解决问题。

官方接口说明
获取用户基本信息(包括UnionID机制) 

Http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.HTML

1、只要有基础的access_token和用户openid就可以判断用户是否关注该公众号
2、利用的接口url为:https://api.weixin.qq.com/cgi-bin/user/info?access_token=$token&openid=$openid
3、判断接口返回的字段subscribe是否为1.【1关注,0未关注】

注:
1、判断用户登录的方式为静默授权,用户无感知,从而得到用户的openid;
2、判断用户登录,需要微信认证服务号的支持,订阅号不行; 

下面是代码案例
 
< ? php

$access_token = $this - > _getAccessToken();
$subscribe_msg = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token='.$access_token.'&openid='.$_<a data-cke-saved-href="http://www.kubiji.cn/juhe_listing-idPHPSESSION.html" href="http://www.kubiji.cn/juhe_listing-idPHPSESSION.html">session</a>['wecha_id'];
$subscribe = <a data-cke-saved-href="http://www.kubiji.cn/juhe_listing-idPHPJSON.html" href="http://www.kubiji.cn/juhe_listing-idPHPJSON.html">json</a>_decode($this - > <a data-cke-saved-href="http://www.kubiji.cn/juhe_listing-idPHPCURL.html" href="http://www.kubiji.cn/juhe_listing-idPHPCURL.html">curl</a>Get($subscribe_msg));
$zyxx = $subscribe - > subscribe;

if ($zyxx !== 1) {
echo'未关注!';
}
private function _getAccessToken() {
$where = array('token' = > $this - > token);
$this - > thisWxUser = M('Wxuser') - > where($where) - > find();
$url_get = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this - > thisWxUser['appid'].'&secret='.$this - > thisWxUser['appsecret'];
$json = json_decode($this - > curlGet($url_get));
if (!$json - > errmsg) {
} else {
$this - > error('获取access_token发生错误:错误代码'.$json - > errcode.',微信返回错误信息:'.$json - > errmsg);
}
return $json - > access_token;
}
? >

 
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持酷笔记

原文链接:http://www.kubiji.cn/topic-id3162.html

PHP编译后添加pdo_mysql模块

回复

chris 发起了问题 • 1 人关注 • 0 个回复 • 551 次浏览 • 2017-01-10 14:34 • 来自相关话题

PHP添加memcache模块

chris 发表了文章 • 0 个评论 • 279 次浏览 • 2017-01-10 03:37 • 来自相关话题

下载memcache模块源码

memcached不用说了,都知道是做数据缓冲用的,这里为 php 添加memcached 的支持。

php的memcache 模块地址 http://pecl.php.net/package/memcache :
# wget http://pecl.php.net/get/memcache-2.2.3.tgz


编译安装源码包

# tar -zxvf memcache-2.2.3.tgz
# cd memcache-2.2.3/
# /usr/local/php5.3.6/bin/phpize
# ./configure –with-php-config=/usr/local/php5.3.6/bin/php-config
# make
# make install
Installing shared extensions: /usr/local/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/编辑php.ini添加extension = “memcache.so”即可。 查看全部


下载memcache模块源码


memcached不用说了,都知道是做数据缓冲用的,这里为 php 添加memcached 的支持。

php的memcache 模块地址 http://pecl.php.net/package/memcache :
# wget http://pecl.php.net/get/memcache-2.2.3.tgz


编译安装源码包


# tar -zxvf memcache-2.2.3.tgz
# cd memcache-2.2.3/
# /usr/local/php5.3.6/bin/phpize
# ./configure –with-php-config=/usr/local/php5.3.6/bin/php-config
# make
# make install
Installing shared extensions: /usr/local/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/
编辑php.ini添加extension = “memcache.so”即可。

为已编译安装的PHP动态添加gd模块

chris 发表了文章 • 0 个评论 • 438 次浏览 • 2017-01-10 03:10 • 来自相关话题

对付单个扩展,单独编译动态加载是比较方便快捷的,安装过程也比较繁杂,这个记录一下,方便后来者。首先说明,我的PHP是源码编译安装的,可以参考:http://www.nowamagic.net/academy/detail/1226244。
 

安装 gd 前置库 : freetype ,jpegsrc,libpng

1、freetype
# wget "http://download.savannah.gnu.org/releases/freetype/freetype-2.4.0.tar.bz2"
# tar jxvf freetype-2.4.0.tar.bz2
# cd freetype-2.4.0
# ./configure --prefix=/usr/local/freetype && make && make install
2、jpegsrc
# wget "http://www.ijg.org/files/jpegsrc.v9.tar.gz"
# tar zxvf jpegsrc.v9.tar.gz
# cd jpeg-9
# CFLAGS="-O3 -fPIC" ./configure --prefix=/usr/local/jpeg && make && make install
# mkdir -p /usr/local/jpeg/include
# mkdir -p /usr/local/jpeg/lib
# mkdir -p /usr/local/jpeg/bin
# mkdir -p /usr/local/jpeg/man/man1
3、libpng
# wget "http://downloads.sourceforge.net/project/libpng/libpng12/1.2.50/libpng-1.2.50.tar.gz"
# tar zxvf libpng-1.2.50.tar.gz
# cd libpng-1.2.50
# CFLAGS="-O3 -fPIC" ./configure --prefix=/usr/local/libpng && make && make install

 

phpize编译安装gd模块

 
举一个例子:
假如你的 PHP 不支持gd库,php的源码包在/usr/local/src/php-5.3.6/,php安装目录在/usr/local/php5.3.6/  
 
执行命令 /usr/local/php/bin/phpize, 什么时候需要用到 phpize 呢?当我们需要再加些模块,又不想重新编译php,这些我们就可以用phpize了。
# cd /usr/local/src/php-5.3.6/ext/gd/
# /usr/local/php5.3.6/bin/phpize现在可以进行单独编译安装了:
# ./configure --with-php-config=/usr/local/php5.3.6/bin/php-config --with-jpeg-dir=/usr/local/jpeg --with-png-dir=/usr/local/libpng --with-freetype-dir=/usr/local/freetype
# make && make install安装成功结果:
[root@web1 gd]# make install
Installing shared extensions: /usr/local/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/
Installing header files: /usr/local/php5.3.6/include/php/
修改php.ini配置文件:
extension_dir = "/usr/local/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/"
extension=gd.so验证:
[root@web1 gd]# /usr/local/php5.3.6/bin/php -m |grep -i gd
gd到这就结束了。 查看全部
对付单个扩展,单独编译动态加载是比较方便快捷的,安装过程也比较繁杂,这个记录一下,方便后来者。首先说明,我的PHP是源码编译安装的,可以参考:http://www.nowamagic.net/academy/detail/1226244
 


安装 gd 前置库 : freetype ,jpegsrc,libpng


1、freetype
# wget "http://download.savannah.gnu.org/releases/freetype/freetype-2.4.0.tar.bz2"
# tar jxvf freetype-2.4.0.tar.bz2
# cd freetype-2.4.0
# ./configure --prefix=/usr/local/freetype && make && make install

2、jpegsrc
# wget "http://www.ijg.org/files/jpegsrc.v9.tar.gz"
# tar zxvf jpegsrc.v9.tar.gz
# cd jpeg-9
# CFLAGS="-O3 -fPIC" ./configure --prefix=/usr/local/jpeg && make && make install
# mkdir -p /usr/local/jpeg/include
# mkdir -p /usr/local/jpeg/lib
# mkdir -p /usr/local/jpeg/bin
# mkdir -p /usr/local/jpeg/man/man1

3、libpng
# wget "http://downloads.sourceforge.net/project/libpng/libpng12/1.2.50/libpng-1.2.50.tar.gz"
# tar zxvf libpng-1.2.50.tar.gz
# cd libpng-1.2.50
# CFLAGS="-O3 -fPIC" ./configure --prefix=/usr/local/libpng && make && make install

 


phpize编译安装gd模块


 
举一个例子:
假如你的 PHP 不支持gd库,php的源码包在/usr/local/src/php-5.3.6/,php安装目录在/usr/local/php5.3.6/  
 
执行命令 /usr/local/php/bin/phpize, 什么时候需要用到 phpize 呢?当我们需要再加些模块,又不想重新编译php,这些我们就可以用phpize了。
# cd /usr/local/src/php-5.3.6/ext/gd/
# /usr/local/php5.3.6/bin/phpize
现在可以进行单独编译安装了:
# ./configure --with-php-config=/usr/local/php5.3.6/bin/php-config  --with-jpeg-dir=/usr/local/jpeg  --with-png-dir=/usr/local/libpng   --with-freetype-dir=/usr/local/freetype
# make && make install
安装成功结果:
[root@web1 gd]# make install
Installing shared extensions: /usr/local/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/
Installing header files: /usr/local/php5.3.6/include/php/

修改php.ini配置文件:
extension_dir = "/usr/local/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/"
extension=gd.so
验证:
[root@web1 gd]# /usr/local/php5.3.6/bin/php -m |grep -i gd
gd
到这就结束了。

老男孩Python开发2016最新14期前10周视频【限免】

Target 回复了问题 • 5 人关注 • 4 个回复 • 2762 次浏览 • 2017-01-07 22:33 • 来自相关话题

python FTP问题

采菊篱下 回复了问题 • 3 人关注 • 2 个回复 • 629 次浏览 • 2016-12-27 11:38 • 来自相关话题

Centos下扩展PHP模块Imagick详解

being 发表了文章 • 0 个评论 • 368 次浏览 • 2016-12-20 20:39 • 来自相关话题

简介

imagick是一个PHP的扩展,它调用ImageMagick提供的API来进行图片的操作

ImageMagick是一套软件系列,主要用于图片的创建、编辑以及转换等,详细的解释见ImageMagick的官方网站http://www.imagemagick.org/  ,ImageMagick与GD的性能要高很多,如果是在处理大量的图片时更加能体现ImageMagick的性能。
 
通常安装安装php的imagick扩展模块有两种方法,一种是利用pcel安装imagick(适用于php verison 5.4 或者更高),第二种就是手动下载编译安装,下面依次介绍。

一、pcel安装imagick

1、首先安装ImageMagick# cd /usr/local/src/
# wget ftp://ftp.u-aizu.ac.jp/pub/graphics/image/ImageMagick/imagemagick.org/ImageMagick-6.8.7-0.tar.gz
# tar zxf ImageMagick-6.8.7-0.tar.gz
# cd ImageMagick-6.8.7-0
# ./configure -prefix=/usr/local/imagemagick
# make && make install官网地址:http://www.imagemagick.org/
 
2、安装imagick# 首先进入到PHP的bin目录
# cd /usr/local/php5.6.26/bin/
# ./pecl install imagick

................
Build process completed successfully
Installing '/usr/local/php5.6.26/lib/php/extensions/no-debug-non-zts-20131226/imagick.so'
Installing '/usr/local/php5.6.26/include/php/ext/imagick/php_imagick_shared.h'
install ok: channel://pecl.php.net/imagick-3.4.3RC1
configuration option "php_ini" is not set to php.ini location
You should add "extension=imagick.so" to php.ini产生的imagick.so文件拷贝到/usr/local/php5.6.26/lib/php/extensions/no-debug-non-zts-20131226下
 
在php.ini文件里添加imagick.so,然后重启php加载imagick模块即可,使用/usr/local/php5.6.26/bin/php -m 命令查看模块是否添加成功。

二、编译安装imagick

1、首先安装ImageMagick 同上
 
2、编译安装imagick# wget http://pecl.php.net/get/imagick-3.1.2.tgz
# tar zxf imagick-3.1.2.tgz
# cd imagick-3.1.2
# /usr/local/php5.3.6/bin/phpize (这个看你php安装路径)
# 注:phpize是一个shell脚本,主要是用来进行编译环境的准备,执行以后会生成一些新的文件,为配置、编译及安装作好准备
# ./configure --with-php-config=/usr/local/php5.3.6/bin/php-config --with-imagick=/usr/local/imagemagick
# make && make install在php配置文件php.ini中添加:extension=imagick.so重启apache或php-fpm就可以了。 查看全部
PHP.png


简介


imagick是一个PHP的扩展,它调用ImageMagick提供的API来进行图片的操作

ImageMagick是一套软件系列,主要用于图片的创建、编辑以及转换等,详细的解释见ImageMagick的官方网站http://www.imagemagick.org/  ,ImageMagick与GD的性能要高很多,如果是在处理大量的图片时更加能体现ImageMagick的性能。
 
通常安装安装php的imagick扩展模块有两种方法,一种是利用pcel安装imagick(适用于php verison 5.4 或者更高),第二种就是手动下载编译安装,下面依次介绍。


一、pcel安装imagick


1、首先安装ImageMagick
# cd /usr/local/src/
# wget ftp://ftp.u-aizu.ac.jp/pub/graphics/image/ImageMagick/imagemagick.org/ImageMagick-6.8.7-0.tar.gz
# tar zxf ImageMagick-6.8.7-0.tar.gz
# cd ImageMagick-6.8.7-0
# ./configure -prefix=/usr/local/imagemagick
# make && make install
官网地址:http://www.imagemagick.org/
 
2、安装imagick
# 首先进入到PHP的bin目录
# cd /usr/local/php5.6.26/bin/
# ./pecl install imagick

................
Build process completed successfully
Installing '/usr/local/php5.6.26/lib/php/extensions/no-debug-non-zts-20131226/imagick.so'
Installing '/usr/local/php5.6.26/include/php/ext/imagick/php_imagick_shared.h'
install ok: channel://pecl.php.net/imagick-3.4.3RC1
configuration option "php_ini" is not set to php.ini location
You should add "extension=imagick.so" to php.ini
产生的imagick.so文件拷贝到/usr/local/php5.6.26/lib/php/extensions/no-debug-non-zts-20131226下
 
在php.ini文件里添加imagick.so,然后重启php加载imagick模块即可,使用/usr/local/php5.6.26/bin/php -m 命令查看模块是否添加成功。


二、编译安装imagick


1、首先安装ImageMagick 同上
 
2、编译安装imagick
# wget http://pecl.php.net/get/imagick-3.1.2.tgz
# tar zxf imagick-3.1.2.tgz
# cd imagick-3.1.2
# /usr/local/php5.3.6/bin/phpize (这个看你php安装路径)
# 注:phpize是一个shell脚本,主要是用来进行编译环境的准备,执行以后会生成一些新的文件,为配置、编译及安装作好准备
# ./configure --with-php-config=/usr/local/php5.3.6/bin/php-config --with-imagick=/usr/local/imagemagick
# make && make install
在php配置文件php.ini中添加:
extension=imagick.so
重启apache或php-fpm就可以了。

Python最差实践变更

chris 发表了文章 • 0 个评论 • 288 次浏览 • 2016-12-20 17:28 • 来自相关话题

最近在看一些陈年老系统,其中有一些不好的代码习惯遗留下来的坑;加上最近自己也写了一段烂代码导致服务器负载飙升,所以就趁此机会总结下我看到过/写过的自认为不好的Python代码习惯,时刻提醒自己远离这些“最差实践”,避免挖坑。

下面所举的例子中,有一部分会造成性能问题,有一部分会导致隐藏bug,或日后维护、重构困难,还有一部分纯粹是我认为不够pythonic。所以大家自行甄别,取精去糟吧。
 

函数默认参数使用可变对象​

这个例子我想大家应该在各种技术文章中见过许多遍了,也足以证明这是一个大坑。
 
先看错误示范吧:
def use_mutable_default_param(idx=0, ids=[]):
ids.append(idx)
print(idx)
print(ids)

use_mutable_default_param(idx=1)
use_mutable_default_param(idx=2)输出:
1
[1]
2
[1, 2]理解这其中的原因,最重要的是有两点:
函数本身也是一个对象,默认参数绑定于这个函数对象上append这类方法会直接修改对象,所以下次调用此函数时,其绑定的默认参数已经不再是空list了
 
正确的做法如下:
def donot_use_mutable_default_param(idx=0, ids=None):
if ids is None:
ids = []
ids.append(idx)
print(idx)
print(ids)

try…except不具体指明异常类型

虽然在Python中使用try…except不会带来严重的性能问题,但是不加区分,直接捕获所有类型异常的做法,往往会掩盖掉其他的bug,造成难以追查的bug。

一般的,我觉得应该尽量少的使用try…except,这样可以在开发期尽早的发现问题。即使要使用try…except,也应该尽可能的指定出要捕获的具体异常,并在except语句中将异常信息记入log,或者处理完之后,再直接raise出来。
 

关于dict的冗余代码

我经常能够看到这样的代码:
d = {}
datas = [1, 2, 3, 4, 2, 3, 4, 1, 5]
for k in datas:
if k not in d:
d[k] = 0
d[k] += 1其实,完全可以使用collections.defaultdict这一数据结构更简单优雅的实现这样的功能:
default_d = defaultdict(lambda: 0)
datas = [1, 2, 3, 4, 2, 3, 4, 1, 5]
for k in datas:
default_d[k] += 1 同样的,这样的代码:
# d is a dict
if 'list' not in d:
d['list'] = []
d['list'].append(x)完全可以用这样一行代码替代:
# d is a dict
d.setdefault('list', []).append(x)同样的,下面这两种写法一看就是带有浓浓的C味儿:
# d is a dict
for k in d:
v = d[k]
# do something

# l is a list
for i in len(l):
v = l[i]
# do something应该用更pythonic的写法:
# d is a dict
for k, v in d.iteritems():
# do something
pass

# l is a list
for i, v in enumerate(l):
# do something
pass另外,enumerate其实还有个第二参数,表示序号从几开始。如果想要序号从1开始数起,可以使用enumerate(l, 1)。 
 

使用flag变量而不使用for…else语句

同样,这样的代码也很常见:
search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey']
found = False
for s in search_list:
if s.startswith('C'):
found = True
# do something when found
print('Found')
break

if not found:
# do something when not found
print('Not found') 其实,用for…else更优雅:
search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey']
for s in search_list:
if s.startswith('C'):
# do something when found
print('Found')
break
else:
# do something when not found
print('Not found')

 

过度使用tuple unpacking

在Python中,允许对tuple类型进行unpack操作,如下所示:
# human = ('James', 180, 32)
name,height,age = human这个特性用起来很爽,比写name=human[0]之类的不知道高到哪里去了。所以,这一特性往往被滥用,一个human在程序的各处通过上面的方式unpack。

然而如果后来需要在human中插入一个表示性别的数据sex,那么对于所有的这种unpack都需要进行修改,即使在有些逻辑中并不会使用到性别。
# human = ('James', 180, 32)
name,height,age, _ = human
# or
# name, height, age, sex = human有如下几种方式解决这一问题:
老老实实写name=human[0]这种代码,在需要使用性别信息处加上sex=human[3]使用dict来表示human使用namedtuple
 
# human = namedtuple('human', ['name', 'height', 'age', 'sex'])
h = human('James', 180, 32, 0)
# then you can use h.name, h.sex and so on everywhere.

到处都是import *

import *是一种懒惰的行为,它不仅会污染当前的命名空间,并且还会使得pyflakes等代码检查工具失效。在后续查看代码或者debug的过程中,往往也很难从一堆import *中找到一个第三方函数的来源。

可以说这种习惯是百害而无一利的。
 

文件操作

文件操作不要使用裸奔的f = open(‘filename’)了,使用with open(‘filename’) as f来让context manager帮你处理异常情况下的关闭文件等乱七八糟的事情多好。
 

野蛮使用class.name判断类型

我曾经遇见过一个bug:为了实现某特定功能,我新写了一个class B(A),在B中重写了A的若干函数。整个实现很简单,但是就是有一部分A的功能无法生效。最后追查到的原因,就是在一些逻辑代码中,硬性的判断了entity.__class__.__name__ == ‘A’。

除非你就是想限定死继承层级中的当前类型(也就是,屏蔽未来可能会出现的子类),否则,不要使用__class__.__name__,而改用isinstance这个内建函数。毕竟,Python把这两个变量的名字都刻意带上那么多下划线,本来就是不太想让你用嘛。
 

循环内部有多层函数调用

循环内部有多层函数调用,有如下两方面的隐患:
Python没有inline函数,所以函数调用本来就会导致一定的开销,尤其是本身逻辑简单的时候,这个开销所占的比例就会挺可观的。更严重的是,在之后维护这份代码时,会容易让人忽略掉函数是在循环中被调用的,所以容易在函数内部添加了一些开销较大却不必每次循环都调用的函数,比如time.localtime()。如果是直接一个平铺直叙的循环,我想大部分的程序员都应该知道把time.localtime()写到循环的外面,但是引入多层的函数调用之后,就不一定了哦。
 
所以我建议,在循环内部,如非特别复杂的逻辑,都应该直接写在循环里,不要进行函数调用。如果一定要包装一层函数调用,应该在函数的命名或注释中,提示后续的维护者,这个函数会在循环内部使用。
 

总结

Python是一门非常容易入门的语言,严格的缩进要求和丰富的内置数据类型,使得大部分Python代码都能做到比较好的规范。但是,不严格要求自己,也很容易就写出犯二的代码。上面列出的只是很小的一部分,唯有多读、多写、多想,才能培养敏锐的代码嗅觉,第一时间发现坏味道啊。
 

分享阅读:http://blog.guoyb.com/2016/12/03/bad-py-style/
作者:yubo 查看全部
python.png
最近在看一些陈年老系统,其中有一些不好的代码习惯遗留下来的坑;加上最近自己也写了一段烂代码导致服务器负载飙升,所以就趁此机会总结下我看到过/写过的自认为不好的Python代码习惯,时刻提醒自己远离这些“最差实践”,避免挖坑。

下面所举的例子中,有一部分会造成性能问题,有一部分会导致隐藏bug,或日后维护、重构困难,还有一部分纯粹是我认为不够pythonic。所以大家自行甄别,取精去糟吧。
 


函数默认参数使用可变对象​


这个例子我想大家应该在各种技术文章中见过许多遍了,也足以证明这是一个大坑。
 
先看错误示范吧:
def use_mutable_default_param(idx=0, ids=[]):
ids.append(idx)
print(idx)
print(ids)

use_mutable_default_param(idx=1)
use_mutable_default_param(idx=2)
输出:
1
[1]
2
[1, 2]
理解这其中的原因,最重要的是有两点:
  1. 函数本身也是一个对象,默认参数绑定于这个函数对象上
  2. append这类方法会直接修改对象,所以下次调用此函数时,其绑定的默认参数已经不再是空list了

 
正确的做法如下:
def donot_use_mutable_default_param(idx=0, ids=None):
if ids is None:
ids = []
ids.append(idx)
print(idx)
print(ids)


try…except不具体指明异常类型


虽然在Python中使用try…except不会带来严重的性能问题,但是不加区分,直接捕获所有类型异常的做法,往往会掩盖掉其他的bug,造成难以追查的bug。

一般的,我觉得应该尽量少的使用try…except,这样可以在开发期尽早的发现问题。即使要使用try…except,也应该尽可能的指定出要捕获的具体异常,并在except语句中将异常信息记入log,或者处理完之后,再直接raise出来。
 


关于dict的冗余代码


我经常能够看到这样的代码:
d = {}
datas = [1, 2, 3, 4, 2, 3, 4, 1, 5]
for k in datas:
if k not in d:
d[k] = 0
d[k] += 1
其实,完全可以使用collections.defaultdict这一数据结构更简单优雅的实现这样的功能:
default_d = defaultdict(lambda: 0)
datas = [1, 2, 3, 4, 2, 3, 4, 1, 5]
for k in datas:
default_d[k] += 1
同样的,这样的代码:
# d is a dict
if 'list' not in d:
d['list'] = []
d['list'].append(x)
完全可以用这样一行代码替代:
# d is a dict
d.setdefault('list', []).append(x)
同样的,下面这两种写法一看就是带有浓浓的C味儿:
# d is a dict
for k in d:
v = d[k]
# do something

# l is a list
for i in len(l):
v = l[i]
# do something
应该用更pythonic的写法:
# d is a dict
for k, v in d.iteritems():
# do something
pass

# l is a list
for i, v in enumerate(l):
# do something
pass
另外,enumerate其实还有个第二参数,表示序号从几开始。如果想要序号从1开始数起,可以使用enumerate(l, 1)。 
 


使用flag变量而不使用for…else语句


同样,这样的代码也很常见:
search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey']
found = False
for s in search_list:
if s.startswith('C'):
found = True
# do something when found
print('Found')
break

if not found:
# do something when not found
print('Not found')
其实,用for…else更优雅:
search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey']
for s in search_list:
if s.startswith('C'):
# do something when found
print('Found')
break
else:
# do something when not found
print('Not found')

 


过度使用tuple unpacking


在Python中,允许对tuple类型进行unpack操作,如下所示:
# human = ('James', 180, 32)
name,height,age = human
这个特性用起来很爽,比写name=human[0]之类的不知道高到哪里去了。所以,这一特性往往被滥用,一个human在程序的各处通过上面的方式unpack。

然而如果后来需要在human中插入一个表示性别的数据sex,那么对于所有的这种unpack都需要进行修改,即使在有些逻辑中并不会使用到性别。
# human = ('James', 180, 32)
name,height,age, _ = human
# or
# name, height, age, sex = human
有如下几种方式解决这一问题:
  1. 老老实实写name=human[0]这种代码,在需要使用性别信息处加上sex=human[3]
  2. 使用dict来表示human
  3. 使用namedtuple

 
# human = namedtuple('human', ['name', 'height', 'age', 'sex'])
h = human('James', 180, 32, 0)
# then you can use h.name, h.sex and so on everywhere.


到处都是import *


import *是一种懒惰的行为,它不仅会污染当前的命名空间,并且还会使得pyflakes等代码检查工具失效。在后续查看代码或者debug的过程中,往往也很难从一堆import *中找到一个第三方函数的来源。

可以说这种习惯是百害而无一利的。
 


文件操作


文件操作不要使用裸奔的f = open(‘filename’)了,使用with open(‘filename’) as f来让context manager帮你处理异常情况下的关闭文件等乱七八糟的事情多好。
 


野蛮使用class.name判断类型


我曾经遇见过一个bug:为了实现某特定功能,我新写了一个class B(A),在B中重写了A的若干函数。整个实现很简单,但是就是有一部分A的功能无法生效。最后追查到的原因,就是在一些逻辑代码中,硬性的判断了entity.__class__.__name__ == ‘A’。

除非你就是想限定死继承层级中的当前类型(也就是,屏蔽未来可能会出现的子类),否则,不要使用__class__.__name__,而改用isinstance这个内建函数。毕竟,Python把这两个变量的名字都刻意带上那么多下划线,本来就是不太想让你用嘛。
 


循环内部有多层函数调用


循环内部有多层函数调用,有如下两方面的隐患:
  1. Python没有inline函数,所以函数调用本来就会导致一定的开销,尤其是本身逻辑简单的时候,这个开销所占的比例就会挺可观的。
  2. 更严重的是,在之后维护这份代码时,会容易让人忽略掉函数是在循环中被调用的,所以容易在函数内部添加了一些开销较大却不必每次循环都调用的函数,比如time.localtime()。如果是直接一个平铺直叙的循环,我想大部分的程序员都应该知道把time.localtime()写到循环的外面,但是引入多层的函数调用之后,就不一定了哦。

 
所以我建议,在循环内部,如非特别复杂的逻辑,都应该直接写在循环里,不要进行函数调用。如果一定要包装一层函数调用,应该在函数的命名或注释中,提示后续的维护者,这个函数会在循环内部使用。
 


总结


Python是一门非常容易入门的语言,严格的缩进要求和丰富的内置数据类型,使得大部分Python代码都能做到比较好的规范。但是,不严格要求自己,也很容易就写出犯二的代码。上面列出的只是很小的一部分,唯有多读、多写、多想,才能培养敏锐的代码嗅觉,第一时间发现坏味道啊。
 


分享阅读:http://blog.guoyb.com/2016/12/03/bad-py-style/
作者:yubo


Python 备份Mysql

Not see︶ 发表了文章 • 0 个评论 • 552 次浏览 • 2016-09-23 19:26 • 来自相关话题

#!/usr/bin/env python
# Filename: mysql_backup.py
# author:kevin yang
# email:zhiwen.yang@showself.com
# date:2016-09-23
import os
import time
import sys
import datetime
from stat import *

# mysql user
User = 'root'

# mysql password
Passwd = '123456'

# mysqldump command
Mysqlcommand = 'mysqldump'

# gzip command
Gzipcommand = '/bin/gzip'

# you want backup mysql database
Mysqldata = ['PHPCMS', 'TeamToy']

# you want backup to dir
Tobackup = '/data/mysql_bak/'

for DB in Mysqldata:
# backup file name
Backfile = Tobackup + DB + '-' + time.strftime('%Y-%m-%d') + '.sql'
# gzip file name
Gzfile = Backfile +'.gz'
if os.path.isfile(Gzfile):
print Gzfile + " is already backup"
else:
# backup command
Back_command = Mysqlcommand + ' -u' + User + ' -p' + Passwd + ' -P3306 ' + DB + ' > ' + Backfile
if os.system(Back_command)==0:
print 'Successful backup to', DB + ' to ' + Backfile
else:
print 'Backup FAILED'
# gzip command
Gzip_command = Gzipcommand + ' ' + Backfile
if os.system(Gzip_command)==0:
print 'Successful Gzip to',Gzfile
else:
print 'Gzip FAILED'

# Delete back file
# show file list
filelist=[]
filelist=os.listdir(Tobackup)
# delete Gzfile 5 days ago
for i in range(len(filelist)):
ft=time.gmtime(os.stat(Tobackup+filelist[i])[ST_MTIME])
ftl=time.strftime('%Y-%m-%d',ft)
year,month,day=ftl.split('-')
ftll=datetime.datetime(int(year),int(month),int(day))
localt=time.gmtime()
localtl=time.strftime('%Y-%m-%d',localt)
year,month,day=localtl.split('-')
localtll=datetime.datetime(int(year),int(month),int(day))
days=(localtll-ftll).days
if days >5:
try:
os.remove(Tobackup+filelist[i])
print 'delete is ok'
except:
log=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')+" remove "+Tobackup+filelist[i]+" fail \n"
print log 查看全部
#!/usr/bin/env python
# Filename: mysql_backup.py
# author:kevin yang
# email:zhiwen.yang@showself.com
# date:2016-09-23
import os
import time
import sys
import datetime
from stat import *

# mysql user
User = 'root'

# mysql password
Passwd = '123456'

# mysqldump command
Mysqlcommand = 'mysqldump'

# gzip command
Gzipcommand = '/bin/gzip'

# you want backup mysql database
Mysqldata = ['PHPCMS', 'TeamToy']

# you want backup to dir
Tobackup = '/data/mysql_bak/'

for DB in Mysqldata:
# backup file name
Backfile = Tobackup + DB + '-' + time.strftime('%Y-%m-%d') + '.sql'
# gzip file name
Gzfile = Backfile +'.gz'
if os.path.isfile(Gzfile):
print Gzfile + " is already backup"
else:
# backup command
Back_command = Mysqlcommand + ' -u' + User + ' -p' + Passwd + ' -P3306 ' + DB + ' > ' + Backfile
if os.system(Back_command)==0:
print 'Successful backup to', DB + ' to ' + Backfile
else:
print 'Backup FAILED'
# gzip command
Gzip_command = Gzipcommand + ' ' + Backfile
if os.system(Gzip_command)==0:
print 'Successful Gzip to',Gzfile
else:
print 'Gzip FAILED'

# Delete back file
# show file list
filelist=[]
filelist=os.listdir(Tobackup)
# delete Gzfile 5 days ago
for i in range(len(filelist)):
ft=time.gmtime(os.stat(Tobackup+filelist[i])[ST_MTIME])
ftl=time.strftime('%Y-%m-%d',ft)
year,month,day=ftl.split('-')
ftll=datetime.datetime(int(year),int(month),int(day))
localt=time.gmtime()
localtl=time.strftime('%Y-%m-%d',localt)
year,month,day=localtl.split('-')
localtll=datetime.datetime(int(year),int(month),int(day))
days=(localtll-ftll).days
if days >5:
try:
os.remove(Tobackup+filelist[i])
print 'delete is ok'
except:
log=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')+" remove "+Tobackup+filelist[i]+" fail \n"
print log

Python程序软件目录规范化

采菊篱下 发表了文章 • 0 个评论 • 574 次浏览 • 2016-09-21 15:18 • 来自相关话题

为什么要设计好目录结构?

"设计项目目录结构",就和"代码编码风格"一样,属于个人风格问题。对于这种风格上的规范,一直都存在两种态度:
一类同学认为,这种个人风格问题"无关紧要"。理由是能让程序work就好,风格问题根本不是问题;另一类同学认为,规范化能更好的控制程序结构,让程序具有更高的可读性。
 
我是比较偏向于后者的,因为我是前一类同学思想行为下的直接受害者。我曾经维护过一个非常不好读的项目,其实现的逻辑并不复杂,但是却耗费了我非常长的时间去理解它想表达的意思。从此我个人对于提高项目可读性、可维护性的要求就很高了。"项目目录结构"其实也是属于"可读性和可维护性"的范畴,我们设计一个层次清晰的目录结构,就是为了达到以下两点:
可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。
 
所以,保持一个层次清晰的目录结构是有必要的。更何况组织一个良好的工程目录,其实是一件很简单的事儿。
 

目录组织方式

关于如何组织一个较好的Python工程目录结构,已经有一些得到了共识的目录结构。在Stackoverflow关于这个问题 ,可以看到很多赞同对Python目录结构规范的情况。

假设你的项目名为foo, 我比较建议的最方便快捷目录结构这样就足够了:Foo/
|-- bin/
| |-- foo
|
|-- foo/
| |-- tests/
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py
|
|-- docs/
| |-- conf.py
| |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README简要解释一下:
bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。foo/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py。docs/: 存放一些文档。setup.py: 安装、部署、打包的脚本。requirements.txt: 存放软件依赖的外部Python包列表。README: 项目说明文件。
 
除此之外,有一些方案给出了更加多的内容。比如LICENSE.txt,ChangeLog.txt文件等,我没有列在这里,因为这些东西主要是项目开源的时候需要用到。如果你想写一个开源软件,目录该如何组织,可以参考开源Python项目的正确方法 。

关于README的内容 

这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。
 
它需要说明以下几个事项:
软件定位,软件的基本功能。运行代码的方法: 安装环境、启动命令等。简要的使用说明。代码目录结构说明,更详细点可以说明软件的基本原理。常见问题说明。
我觉得有以上几点是比较好的一个README。在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,并不是一定要在一开始就将所有信息都补全。但是在项目完结的时候,是需要撰写这样的一个文档的。
 
可以参考Redis源码中Readme的写法,这里面简洁清晰的描述了Redis功能和源码结构。

关于requirements.txt和setup.py

setup.py
 
一般来说,用setup.py来管理代码的打包、安装、部署问题。业界标准的写法是用Python流行的打包工具setuptools 来管理这些事情。这种方式普遍应用于开源项目中。不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定要有一个安装部署工具,能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。
 
我想大多数人是踩过坑的,刚开始接触Python写项目的时候,安装环境、部署代码、运行程序这个过程全是手动完成,遇到过以下问题:
安装环境时经常忘了最近又添加了一个新的Python包,结果一到线上运行,程序就出错了。Python包的版本依赖问题,有时候我们程序中使用的是一个版本的Python包,但是官方的已经是最新的包了,通过手动安装就可能装错了。如果依赖的包很多的话,一个一个安装这些依赖是很费时的事情。新同学开始写项目的时候,将程序跑起来非常麻烦,因为可能经常忘了要怎么安装各种依赖。
setup.py可以将这些事情自动化起来,提高效率、减少出错的概率。"复杂的东西自动化,能自动化的东西一定要自动化。"是一个非常好的习惯。

setuptools的文档比较庞大,刚接触的话,可能不太好找到切入点。学习技术的方式就是看他人是怎么用的,可以参考一下Python的一个Web框架,flask是如何写的:setup.py 。
 
当然,简单点自己写个安装脚本(deploy.sh)替代setup.py也未尝不可。

requirements.txt

这个文件存在的目的是:
方便开发者维护软件的包依赖。将开发过程中新增的包添加进这个列表中,避免在setup.py安装依赖时漏掉软件包。方便读者明确项目使用了哪些Python包。
这个文件的格式是每一行包含一个包依赖的说明,通常是flask>=0.10这种格式,要求是这个格式能被pip识别,这样就可以简单的通过 pip install -r requirements.txt来把所有Python包依赖都装好了。具体格式说明参考 。
 
关于配置文件的使用方法
注意,在上面的目录结构中,没有将conf.py放在源码目录下,而是放在docs/目录下。
 
很多项目对配置文件的使用做法是:
配置文件写在一个或多个python文件中,比如此处的conf.py。项目中哪个模块用到这个配置文件就直接通过import conf这种形式来在代码中使用配置。
 
这种做法我不太赞同:
这让单元测试变得困难(因为模块内部依赖了外部配置)另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径。程序组件可复用性太差,因为这种贯穿所有模块的代码硬编码方式,使得大部分模块都依赖conf.py这个文件。
 
所以,我认为配置的使用,更好的方式:
模块的配置都是可以灵活配置的,不受外部配置文件的影响。程序的配置也是可以灵活控制的。
 
能够佐证这个思想的是,用过nginx和mysql的同学都知道,nginx、mysql这些程序都可以自由的指定用户配置。

所以,不应当在代码中直接import conf来使用配置文件。上面目录结构中的conf.py,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。可以通过给main.py启动参数指定配置路径的方式来让程序读取配置内容。当然,这里的conf.py你可以换个类似的名字,比如settings.py。或者你也可以使用其他格式的内容来编写配置文件,比如settings.yaml之类的。 查看全部


为什么要设计好目录结构?


"设计项目目录结构",就和"代码编码风格"一样,属于个人风格问题。对于这种风格上的规范,一直都存在两种态度:
  1. 一类同学认为,这种个人风格问题"无关紧要"。理由是能让程序work就好,风格问题根本不是问题;
  2. 另一类同学认为,规范化能更好的控制程序结构,让程序具有更高的可读性。

 
我是比较偏向于后者的,因为我是前一类同学思想行为下的直接受害者。我曾经维护过一个非常不好读的项目,其实现的逻辑并不复杂,但是却耗费了我非常长的时间去理解它想表达的意思。从此我个人对于提高项目可读性、可维护性的要求就很高了。"项目目录结构"其实也是属于"可读性和可维护性"的范畴,我们设计一个层次清晰的目录结构,就是为了达到以下两点:
  1. 可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。
  2. 可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。

 
所以,保持一个层次清晰的目录结构是有必要的。更何况组织一个良好的工程目录,其实是一件很简单的事儿。
 


目录组织方式


关于如何组织一个较好的Python工程目录结构,已经有一些得到了共识的目录结构。在Stackoverflow关于这个问题 ,可以看到很多赞同对Python目录结构规范的情况。

假设你的项目名为foo, 我比较建议的最方便快捷目录结构这样就足够了:
Foo/
|-- bin/
| |-- foo
|
|-- foo/
| |-- tests/
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py
|
|-- docs/
| |-- conf.py
| |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README
简要解释一下:
  1. bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
  2. foo/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py。
  3. docs/: 存放一些文档。
  4. setup.py: 安装、部署、打包的脚本。
  5. requirements.txt: 存放软件依赖的外部Python包列表。
  6. README: 项目说明文件。

 
除此之外,有一些方案给出了更加多的内容。比如LICENSE.txt,ChangeLog.txt文件等,我没有列在这里,因为这些东西主要是项目开源的时候需要用到。如果你想写一个开源软件,目录该如何组织,可以参考开源Python项目的正确方法 。


关于README的内容 


这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。
 
它需要说明以下几个事项:
  1. 软件定位,软件的基本功能。
  2. 运行代码的方法: 安装环境、启动命令等。
  3. 简要的使用说明。
  4. 代码目录结构说明,更详细点可以说明软件的基本原理。
  5. 常见问题说明。

我觉得有以上几点是比较好的一个README。在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,并不是一定要在一开始就将所有信息都补全。但是在项目完结的时候,是需要撰写这样的一个文档的。
 
可以参考Redis源码中Readme的写法,这里面简洁清晰的描述了Redis功能和源码结构。


关于requirements.txt和setup.py


setup.py
 
一般来说,用setup.py来管理代码的打包、安装、部署问题。业界标准的写法是用Python流行的打包工具setuptools 来管理这些事情。这种方式普遍应用于开源项目中。不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定要有一个安装部署工具,能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。
 
我想大多数人是踩过坑的,刚开始接触Python写项目的时候,安装环境、部署代码、运行程序这个过程全是手动完成,遇到过以下问题:
  1. 安装环境时经常忘了最近又添加了一个新的Python包,结果一到线上运行,程序就出错了。
  2. Python包的版本依赖问题,有时候我们程序中使用的是一个版本的Python包,但是官方的已经是最新的包了,通过手动安装就可能装错了。
  3. 如果依赖的包很多的话,一个一个安装这些依赖是很费时的事情。
  4. 新同学开始写项目的时候,将程序跑起来非常麻烦,因为可能经常忘了要怎么安装各种依赖。

setup.py可以将这些事情自动化起来,提高效率、减少出错的概率。"复杂的东西自动化,能自动化的东西一定要自动化。"是一个非常好的习惯。

setuptools的文档比较庞大,刚接触的话,可能不太好找到切入点。学习技术的方式就是看他人是怎么用的,可以参考一下Python的一个Web框架,flask是如何写的:setup.py 。
 
当然,简单点自己写个安装脚本(deploy.sh)替代setup.py也未尝不可。


requirements.txt


这个文件存在的目的是:
  1. 方便开发者维护软件的包依赖。将开发过程中新增的包添加进这个列表中,避免在setup.py安装依赖时漏掉软件包。
  2. 方便读者明确项目使用了哪些Python包。

这个文件的格式是每一行包含一个包依赖的说明,通常是flask>=0.10这种格式,要求是这个格式能被pip识别,这样就可以简单的通过 pip install -r requirements.txt来把所有Python包依赖都装好了。具体格式说明参考 。
 
关于配置文件的使用方法
注意,在上面的目录结构中,没有将conf.py放在源码目录下,而是放在docs/目录下。
 
很多项目对配置文件的使用做法是:
  1. 配置文件写在一个或多个python文件中,比如此处的conf.py。
  2. 项目中哪个模块用到这个配置文件就直接通过import conf这种形式来在代码中使用配置。

 
这种做法我不太赞同:
  1. 这让单元测试变得困难(因为模块内部依赖了外部配置)
  2. 另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径。
  3. 程序组件可复用性太差,因为这种贯穿所有模块的代码硬编码方式,使得大部分模块都依赖conf.py这个文件。

 
所以,我认为配置的使用,更好的方式:
  1. 模块的配置都是可以灵活配置的,不受外部配置文件的影响。
  2. 程序的配置也是可以灵活控制的。

 
能够佐证这个思想的是,用过nginx和mysql的同学都知道,nginx、mysql这些程序都可以自由的指定用户配置。

所以,不应当在代码中直接import conf来使用配置文件。上面目录结构中的conf.py,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。可以通过给main.py启动参数指定配置路径的方式来让程序读取配置内容。当然,这里的conf.py你可以换个类似的名字,比如settings.py。或者你也可以使用其他格式的内容来编写配置文件,比如settings.yaml之类的。