chinapnr / fishbase Goto Github PK
View Code? Open in Web Editor NEW自主开发、整理的一套 Python 基础函数库,涵盖 system 系统增强包、logger 日志记录增强包、file 文件处理增强包、 date 日期处理函数包、data 数据信息处理函数包、csv 处理增强函数包、crypt 加密/编码增强包等,可减少程序开发工作量、降低引用复杂度。
License: MIT License
自主开发、整理的一套 Python 基础函数库,涵盖 system 系统增强包、logger 日志记录增强包、file 文件处理增强包、 date 日期处理函数包、data 数据信息处理函数包、csv 处理增强函数包、crypt 加密/编码增强包等,可减少程序开发工作量、降低引用复杂度。
License: MIT License
# URL ?与参数部分的地址拼接
# 输入:
# data: ? 参数名与 value 值组合的 dict
# 输出:
# urls:? 与参数拼接后的字符串
# 2018.3.8 edit by qingqing.xiang
def parse_url(data):
item = data.items()
urls = "?"
for key, value in item:
temp_str = key + "=" + value
urls = urls + temp_str + "&"
# 去掉最后一个&字符
urls = urls[:len(urls) - 1]
return urls
含有数字的字符串self.contain_number_elements
不含有数字字符的字符串self.normal
不是字符串的变量self.not_string
>>> ''.join(random.sample(string.ascii_letters+string.digits,6))
lt5nCQ
jman和fishbase中都有判断是否参数为数字的方法,有时候还需要进一步判断长度,而且不同参数的长度规则不一样,所以抽象参数长度判断。检查的最大和最小长度均可以通过参数传入,通过len来进行进一步判断。
参考代码如下:
def check_number_len(check_param, min_length=None, max_length=None):
"""
check_number_len,判断变量是否为数字,以及大小是否符合在要求长度内
:param
* check_param: (string) 需要判断的变量
* min_length: (int) 变量的最小长度,默认为None
* max_length: (int) 变量的最大长度,默认为None
:return:
* True 长度在要求长度内
* False 长度不在要求长度内
举例如下::
print('--- check_number_len demo ---')
# 定义待代理字符串
check_param = '1234567890'
# 最小长度
min_length = 0
# 最大长度
max_length = 64
check_number_len_result = check_number_len(check_param, min_length, max_length)
print('check_number_len_result right result is : {}'.format(check_number_len_result))
print('---'*5)
check_param = 1234567890
check_number_len_result = check_number_len(check_param, min_length, max_length)
print('check_number_len_result right result is : {}'.format(check_number_len_result))
print('---'*5)
check_param = {'a':'123'}
check_number_len_result = check_number_len(check_param, min_length, max_length)
print('check_number_len_result type error result is : {}'.format(check_number_len_result))
print('---'*5)
check_param = '1234567890'*10
check_number_len_result = check_number_len(check_param, min_length, max_length)
print('check_number_len_result max_length error result is : {}'.format(check_number_len_result))
print('---'*5)
min_length = 20
check_number_len_result = check_number_len(check_param, min_length, max_length)
print('check_number_len_result min_length error result is : {}'.format(check_number_len_result))
print('---'*5)
执行结果::
--- check_number_len demo ---
check_number_len_result right result is : True
---------------
check_number_len_result right result is : True
---------------
check_number_len_result type error result is : False
---------------
check_number_len_result max_length error result is : False
---------------
check_number_len_result min_length error result is : False
---------------
"""
# 判断如果不为数字类型,则返回False
if type(check_param) != int and (not str(check_param).isdigit()):
return False
length = len(str(check_param))
# 参数最大值不为 None 则判断是否超过最大值
if max_length is not None and length > max_length:
return False
# 参数最小值不为 None 则判断是否小于最小值
if min_length is not None and length < min_length:
return False
return True
参考代码如下
import time
from functools import wraps
def fn_timer(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
start_time = time.time()
result = fn(*args, **kwargs)
end_time = time.time() - start_time
print("func {0} run time is {1}".format(fn.__name__, str(end_time - start_time)))
return result
return wrapper
"""
举例如下::
print('--- fn_timer demo---')
@fn_timer
def get_sum(n):
return sum(range(n))
get_sum(100000)
print('---')
执行结果::
--- fn_timer demo---
func get_sum run time is -1528297140.3855693
---
"""
代码如下:
def get_sorted_values_list_from_dict(param_dict, order='asc'):
"""
get_sorted_values_list_from_dict,根据键ascii值升降序拼接字典值的列表
:param
* param_dict: (dict) 字典信息
* order: (string) 键排序规则 enum ('asc', 'desc'),默认 'asc' 升序
:return:
* sorted_values_list: (list) 字典值排序列表
举例如下::
print('--- get_sorted_values_str_from_dict demo ---')
# 定义待处理字典
dict2 = {'a_key':'a_value','1_key':'1_value','A_key':'A_value', 'z_key':'z_value'}
# 升序结果
order = 'asc'
sorted_values_str = get_sorted_values_str_from_dict(dict2, order)
print('sorted_values_list with asc order result is : {}'.format(sorted_values_str))
# 降序结果
order = 'desc'
sorted_values_str = get_sorted_values_str_from_dict(dict2, order)
print('sorted_values_list with desc order result is : {}'.format(sorted_values_str))
print('---')
执行结果::
--- get_sorted_values_str_from_dict demo ---
sorted_values_list with asc order result is :['1_value', 'A_value', 'a_value', 'z_value']
sorted_values_list with desc order result is : ['z_value', 'a_value', 'A_value', '1_value']
---
"""
key_list = list(param_dict.keys())
reverse_flag = False if order == 'asc' else True
key_list.sort(reverse=reverse_flag)
sorted_values_list = [param_dict[x] for x in key_list]
return sorted_values_list
从配置文件读取对应信息
代码如下:
输入 date_dict 类型,指定 year,half_year, quarter, month, week
输出指定日期范围的开始和结束日期
含有特殊字符的字符串self.contain_special_elements
不含有特殊字符的字符串self.normal
不是字符串的变量self.not_string
#63 中只能删除列表中的重复元素,并且因为使用set
,导致去重后的列表中的元素是无序的
key
用以指定一个函数将序列中的元素转换为可哈希类型,这样就可以检测重复元素了set
类型,如果发现不是重复的元素,就将它加入到set
中,以保持有序>>> list1 = [1,23,4,5,6,1,7,23]
>>> list(remove_duplicate_elements(list1))
[1, 23, 4, 5, 6, 7]
>>> list2 = [{'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}]
>>> list(remove_duplicate_elements(a, key=lambda d:(d['x'],d['y'])))
[{'x': 1, 'y': 2}, {'x': 1, 'y': 3}]
写脚本的过程中,经常遇到需要递归的获取某个路径下所有文件长路径,
该方法使用os模块的walk方法,获取目标路径下所有文件的长路径并以列表的形式返回。
参考代码如下:
def file_complete_path(path):
"""
file_complete_path,获取指定路径下所有文件的完整路径
:param
* path: (string) 绝对路径
:return:
* file_list: (list) 文件的完整路径列表
使用场景::
测试脚本中,需要获取某个目录的所有文件
举例如下::
print('--- file_complete_path demo---')
print('on windows')
print(file_complete_path(os.path.split(os.path.realpath(__file__))[0]))
print('on linux')
print(file_complete_path(os.path.split(os.path.realpath(__file__))[0]))
print('---')
执行结果::
--- file_complete_path demo---
on windows
['E:\\Practice\\fishbase_issue\\jman_issue.py', 'E:\\Practice\\fishbase_issue\\path_test\\__init__.py']
on linux
['/root/script/test.png', '/root/script/result.txt','/root/script/path_test/__init__.py']
---
"""
file_list = []
for root, dirs, files in os.walk(path):
for name in files:
file_list.append(os.path.join(root, name))
return file_list
输入年份和星期序号,返回该星期的开始和结束日期
在unittest.conf中写入:
test_md5=06e0e6637d27b2622ab52022db713ce2 #此值为字符串'HelloWorld!'的md5值
Python logging 是线程安全的,也就是说,在一个进程内的多个线程同时往同一个文件写日志是安全的。但是多个进程往同一个文件写日志不是安全的,参见官方文档: https://docs.python.org/2/library/logging.html#thread-safety
kyoto项目多进程启动服务,在进行日志记录时常常会出现莫名少了一些日志,并且日志文件日志写入时间跟文件名不一致。
当文件需要分割时,一个进程先更改了文件名,例如:test.log.2018.5.7,其他进程在进行文件分割时发现 test.log.2018.5.7 已经存在,就会删除,然后重新命名文件为 test.log.2018.5.7,这样之前进程记录的日志就丢失了。
具体进程不安全的代码如下,参见 Python logging模块中 TimedRotatingFileHandler类中 按照时间分割日志文件的部分
def doRollover(self):
"""
Do a rollover, as described in __init__().
"""
if self.stream:
self.stream.close()
self.stream = None
if self.backupCount > 0:
for i in range(self.backupCount - 1, 0, -1):
sfn = self.rotation_filename("%s.%d" % (self.baseFilename, i))
dfn = self.rotation_filename("%s.%d" % (self.baseFilename,
i + 1))
if os.path.exists(sfn):
if os.path.exists(dfn):
os.remove(dfn)
os.rename(sfn, dfn)
dfn = self.rotation_filename(self.baseFilename + ".1")
if os.path.exists(dfn):
os.remove(dfn)
self.rotate(self.baseFilename, dfn)
if not self.delay:
self.stream = self._open()
上面文件删除代码有多进程问题
if os.path.exists(dfn):
os.remove(dfn)
参见 http://python.jobbole.com/87300/ 的方法,重写 TimedRotatingFileHandler 类中删除文件的逻辑改为:
#判断当 test.log.2018.5.7 不存在时重命名,而不是存在时删除文件
#dfn 为将要切割的文件名,eg test.log.2018.5.7, baseFilename 为当前记录日志文件名, eg test.log
if not os.path.exists(dfn) and os.path.exists(self.baseFilename):
os.rename(self.baseFilename, dfn)
最终实现,通过子类SafeRotatingFileHandler,继承TimedRotatingFileHandler,实现以上修改,然后在fish_base模块中logger.py使用SafeRotatingFileHandler即可
class SafeRotatingFileHandler(TimedRotatingFileHandler):
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
TimedRotatingFileHandler.__init__(self, filename, when, interval, backupCount, encoding, delay, utc)
def doRollover(self):
"""
do a rollover; in this case, a date/time stamp is appended to the filename
when the rollover happens. However, you want the file to be named for the
start of the interval, not the current time. If there is a backup count,
then we have to get a list of matching filenames, sort them and remove
the one with the oldest suffix.
"""
if self.stream:
self.stream.close()
self.stream = None
# get the time that this sequence started at and make it a TimeTuple
currentTime = int(time.time())
dstNow = time.localtime(currentTime)[-1]
t = self.rolloverAt - self.interval
if self.utc:
timeTuple = time.gmtime(t)
else:
timeTuple = time.localtime(t)
dstThen = timeTuple[-1]
if dstNow != dstThen:
if dstNow:
addend = 3600
else:
addend = -3600
timeTuple = time.localtime(t + addend)
dfn = self.rotation_filename(self.baseFilename + "." +
time.strftime(self.suffix, timeTuple))
if not os.path.exists(dfn) and os.path.exists(self.baseFilename):
os.rename(self.baseFilename, dfn)
self.rotate(self.baseFilename, dfn)
if self.backupCount > 0:
for s in self.getFilesToDelete():
os.remove(s)
if not self.delay:
self.stream = self._open()
newRolloverAt = self.computeRollover(currentTime)
while newRolloverAt <= currentTime:
newRolloverAt = newRolloverAt + self.interval
#If DST changes and midnight or weekly rollover, adjust for this.
if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
dstAtRollover = time.localtime(newRolloverAt)[-1]
if dstNow != dstAtRollover:
if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
addend = -3600
else: # DST bows out before next rollover, so we need to add an hour
addend = 3600
newRolloverAt += addend
self.rolloverAt = newRolloverAt
代码如下:
def is_timestamp(timestamp):
"""
检查时间戳
:param timestamp: 时间戳
:return: 整形长度为10或者数字字符串并且长度为10时返回True
其他情况返回False
"""
if isinstance(timestamp, int):
return True if len(str(timestamp)) == 10 else False
elif isinstance(timestamp, str):
if timestamp.isdigit() and len(timestamp) == 10:
return True
else:
return False
reStructuredText 超链接写法参考如下:
`#75 <https://github.com/chinapnr/fishbase/issues/75>`_
注意: 最后面有个下划线"_"
效果如下:
#75
增加 get_long_filename_with_sub_dir() 函数。
用于生成当前路径下的一级子路径的某个文件的长文件名。
\aaa.py
\bbb\ccc.conf
use in aaa.py
get_long_filename_with_sub_dir('bbb', 'ccc.conf')
then you can get
C:....\bbb\ccc.conf
相关代码如下:
def get_md5(string, mode='S'):
"""
获取MD5方法
:param string: 待hash的字符串,或者是文件的路径,
如果是文件路径的时候需要设置mode='F'
:param mode: 模式,'S', 获取字符串的MD5
'F', 传入的str需要是文件的路径
'B', 大文件的MD5,传入的str需要是文件的路径
:return: 32位小写MD5值
"""
if mode.upper() == 'S':
return _get_str_md5(string)
if mode.upper() == 'F':
return _get_file_md5(string)
if mode.upper() == 'B':
return _get_big_file_md5(string)
def _get_str_md5(string):
"""
获取一个字符串的MD5值
:param string 待hash的字符串
:return: 32位小写MD5值
"""
m0 = hashlib.md5()
m0.update(string.encode('utf-8'))
result = m0.hexdigest()
return result
def _get_file_md5(file_path):
"""
获取一个文件的MD5值
:param file_path 待hash的文件路径
:return: 32位小写MD5值
"""
with open(file_path, 'rb') as f:
md5obj = hashlib.md5()
md5obj.update(f.read())
hash_o = md5obj.hexdigest()
return hash_o
def _get_big_file_md5(file_name):
"""
获取一个较大文件的MD5值
:param file_name 待hash的文件路径
:return: 32位小写MD5值
"""
if not os.path.isfile(file_name):
return
hash_o = hashlib.md5()
f = open(file_name, 'rb')
while True:
b = f.read(8096)
if not b:
break
hash_o.update(b)
f.close()
return hash_o.hexdigest()
Such as:
.cache/* # 缓存文件
.coverage # coverage命令 自动生成的缓存文件
.coveragerc
fishbase.egg-info/* # 使用 python setup.py sdist 打包时自动生成的,用来存放package信息的文件
readme # 空文件
list
转换成set
,去除掉重复元素,再转换成list
返回>>> list1 = ['a', 1, 2, 'c', 'd', 'sting', 2, 'd', 'string']
>>> remove_duplicate_elements(list1)
[1, 2, 'd', 'c', 'a', 'string']
定义:
空字符串self.empty
含有空格字符的字符串self.cotain_space
含有None类型的字符串self.contain_none
不含有前面三种情况的字符串self.normal
不是字符串的其他类型的变量self.not_string
在unittest.conf中写入:
platform=win32 #在Linux环境下改为linux,在Mac OS环境下改为macos
post
请求中需要判断body
的参数字典是否存在空的值Python
中if
判断为False
的值dict.keys()
方法取得所有的键,根据dict.get()
来进行判断是否值为空>>> dict1 = {'A_key': 'A_value', 'a_key': 'a_value', 'z_key': 'z_value'}
>>> is_dict_value_none(dict1)
False
>>> dict1.update({'non_str':''})
>>> is_dict_value_none(dict1)
True
>>> dict1.update({'non_list':[]})
>>> is_dict_value_none(dict1)
True
Base64
是网络上最常见的用于传输8Bit字节码的编码方式之一Base64
进行编码GetMD5
实现file
和string
,两个静态方法,分别对file
和string
进行Base64
编码>>> GetBase64.string('hello world')
b'aGVsbG8gd29ybGQ='
>>> GetBase64.file('./test.png')
b'IyEvYmluL2Jhc2gKCmNkIC9yb290L3d3dy9zaW5nbGVfcWEKCm5vaHVwIC9yb290L2FwcC9weXRob24zNjIvYmluL2d1bmljb3JuIC1jIGd1bmljb3JuLmNvbmYgc2luZ2xlX3NlcnZlcjphcHAK'
类 fish_common.GetMD5
中方法file
和big_file
重复,另外对于文件大小的划分没有标准,多大为大文件?
计算文件的md5
值时均使用分块读入,使用big_file
替换file
resize_image
用来对图片进行缩放函数命名及参数暂定如下:
resize_image(img_path, mini_size=480, jpeg_quality=80)
mini_size为最小边像素数,默认为480,jpeg_quality为jpeg的图片质量,默认为 80,即0.8
用来对图片进行缩放
需要引入新的依赖wand
包, 详见 https://pypi.org/project/Wand/#files
web
中需要根据分页获取数据all_records
,页码数:page_number
和每页数据量:page_size
作为参数page_number
默认值为1
, page_size
默认值为10
all_records
类型是列表,page_number
和page_size
类型为整形>>> all_records = [1,2,3,4,5]
>>> get_list_offset_data(all_records)
[1, 2, 3, 4, 5]
>>> all_records1 = list(range(100))
>>> get_list_offset_data(all_records1, page_number=5, page_size=15)
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74]
>>> get_list_offset_data(all_records1, page_number=7, page_size=15)
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
只含有字母的字符串self.contain_letter_elements
不只含有数字字符的字符串self.normal
不是字符串的变量self.not_string
在一个完全干净的 python 环境安装 fishbase 出错,比较奇怪。
Collecting fishbase
Downloading https://files.pythonhosted.org/packages/a4/2b/45c42409bbb090cb5df5fbabda0b13189402070d00174f06755c51839d25/fishbase-1.0.13.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/setup.py", line 3, in
from fishbase import version
File "/private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/fishbase/init.py", line 21, in
from .fish_date import *
File "/private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/fishbase/fish_date.py", line 4, in
from dateutil.relativedelta import relativedelta
ModuleNotFoundError: No module named 'dateutil'
----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/
在 setup.py 中是有
install_requires=['python-dateutil']
应该会自动安装 python-dateutil,也就是 dateutil
需要验证是否哪里有问题
HMAC是密钥相关的哈希运算消息认证码,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。其实和我们在计算md5中加入盐值的原理是一致的 md5(message + salt),只不过HMAC是一个标准的算法,支持各种hash算法。比如我们这里使用的hmac_sha256, 其实就是在计算sha256的时候加入一个盐值,通常运用在一些具有较高安全要求的hash认证中。
参考代码如下:
def hmac_sha256(secret, message):
"""
hmac_sha256
:param secret: 密钥
:param message: 消息输入
:return: 长度为64的小写hex string 类型的hash值
"""
hash_o = hmac.new(secret.encode('utf-8'),
message.encode('utf-8'),
digestmod=hashlib.sha256).digest().hex()
return hash_o
get_date_range
方法用于获取本月、上月的第一天和最后一天日期,函数名修改为get_month_date_range
更加清晰201809
,也可以是datetime
类型,例如:datetime.datetime.now()
-
和 /
作为分隔符>>> Fday, Lday = get_month_date_range('201809', delimiter='/')
>>> Fday
'2018/09/01'
>>> Lday
'2018/09/30'
>>> make_month = datetime.date(day=1, month=2, year=2018)
>>> Fday1, Lday1 = get_month_date_range(make_month)
>>> Fday1
'2018-02-01'
>>> Lady1
'2018-02-28'
fish_common.splice_url_params
方法用于拼接url
的query
参数Python
内建包urllib提供了丰富的方法用于url
的解析OrderedDict
来保持字典的顺序>>> dict1 = {'spam': 1, 'eggs': 2, 'bacon': 0}
>>> od = OrderedDict(sorted(dict1.items()))
>>> urllib.parse.urlencode(od)
'bacon=0&eggs=2&spam=1'
增加bitoos类方法change_card_number_to_mcc,通过截取银行卡号的前 8,6,5,4,3,2 位,转换成配置文件对应的mcc
sorted
函数可以接受一个用来传递可调用对象的参数key
sorted_objs_by_attr
提供参数key
,用来标识排序的规则>>> class User(object):
def __init__(self, user_id):
self.user_id = user_id
>>> users = [User(23), User(3), User(99)]
>>> [user.user_id for user in users]
[23, 3, 99]
>>> sorted_result = sorted_objs_by_attr(users, key='user_id')
>>> [user.user_id for user in sorted_result]
[3, 23, 99]
参考datetime.timedelta写法,方法获取月份增量后年月字符串。
主要用于根据年月信息查询数据时,年月信息的计算。
参考代码如下:
# months of a year
MONTH = 12
def previous_months_date(n):
"""
previous_months_date,获得n个月前的日期(年月)
:param
* n: (int) n个月前,正整数
:return:
* date: (string) n个月前的年月
使用场景::
获取n个月前的日期,以便查询n个月前的数据
举例如下::
print('--- previous_months_date demo---')
print('last month:', previous_months_date(1))
print('10 months ago:', previous_months_date(10))
print('---')
执行结果::
--- previous_months_date demo---
last month: 201804
10 months ago: 201707
---
"""
now = datetime.now()
all_months = now.year * MONTH + now.month
all_months -= n
year, month = divmod(all_months, MONTH)
if month == 0:
year -= 1
month = MONTH
date = ''.join(['%04d' % year, '%02d' % month])
return date
字符串的校验需要检查特殊字符,目前封装中文和数字类型,后续预留扩展。
检查字符串是否包含特殊字符,用正则表达式进行判断,如果包含特殊字符,正则search的结果不为空。
参考代码如下:
def if_any_char_is_zh(p_str):
"""
if_any_char_is_letter,字符串变量是否含有中文
:param
* p_str: (string) 需要判断的字符串变量
:return:
* True 含有中文字符串
* False 不含有中文字符串
举例如下::
print('--- if_any_char_is_zh demo---')
non_zh_result = if_any_char_is_zh('meiyouzhongwen')
zh_result = if_any_char_is_zh('没有zhongwen')
print('non_zh_result result is {}'.format(non_zh_result))
print('zh_result result is {}'.format(zh_result))
print('---')
执行结果::
--- if_any_char_is_zh demo---
non_zh_result result is False
zh_result result is True
---
"""
zh_pattern = re.compile(u'[\u4e00-\u9fa5]+')
if zh_pattern.search(p_str):
return True
else:
return False
sqlalchemy
查询结果是一个orm
的对象,在取到orm
对象之后需要取出表对象的字段属性public
属性取出getattr
获得对象的属性>>> class Table(object):
def __init__(self, a, b, c):
self._d = 12
self.a = a
self.b = b
self.c = c
>>> table = Table(1,2,3)
>>> get_obj_attr_dict(table)
{'a': 1, 'b': 2, 'c': 3}
根据PEP 396的规范, version的版本需要写到__version__属性下。
PEP 396官方链接
- When a module (or package) includes a version number, the version SHOULD be available in the __version__ attribute.
增加基础类bitools,用于放置bi分析的方法。包含up_init()、change_card_number_to_mcc两个方法。
def json_contained(src_json, tar_json):
# 判断接口返回值与预期返回值是否一致
key_list = src_json.keys()
for key in key_list:
if not tar_json.get(key) == src_json.get(key):
return False
return True
min_len=0, max_len=sys.maxsize
,分别表示最小长度为0,最大长度为当前环境支持最大Int值hasattr(obj, '__len__')
方法判断校验对象是否能取长度值,不能则返回 TypeError
min_len < len(obj) < max_len
判断长度>>> list1 = 'abdaca'
>>> int1 = 12
>>> check_obj_length(list1)
True
>>> check_obj_length(list1, min_len=10, max_len=20)
False
>>> check_obj_length(int1)
TypeError: object type of <class 'int'> has no len()
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.