将Replit的Python脚本迁移到本地TabPy服务器时遇'DataFrame不可调用'错误
问题描述
我编写了一段Python脚本调用美国国家海洋和大气管理局(NOAA)接口,通过邮政编码获取气象站列表并转换为Pandas DataFrame。该脚本在Replit上运行正常,但迁移到本地TabPy服务器后,尽管日志显示DataFrame已正常构建,却在Tableau Prep中出现TypeError : 'DataFrame' object is not callable错误。脚本代码如下:
import requests; import pandas as pd; import json; zip = '97034' userToken = 'foobar123' headerCreds = dict(token = userToken) url = 'https://www.ncei.noaa.gov/cdo-web/api/v2/stations?&locationid=ZIP:' + zip global dfWorking def get_stations_for_zip(): r = requests.get(url, headers = headerCreds) data = json.loads(r.text) if 'results' in data: data = data.get('results') dfWorking = pd.DataFrame(data) # Column datatypes as received # elevation float64 # mindate object # maxdate object # latitude float64 # name int64 # datacoverage float64 # id object # elevationUnit object # longitude float64 dfWorking = dfWorking.astype({'name': 'str'}) # dfWorking['name'] = dfWorking.index # defining an index converts back to float64 print(dfWorking) else: print('no results object in response') return dfWorking # Note: the below prep functions are undefined until they are on a TabPy server def get_output_schema(): return pd.DataFrame({ 'elevation' : prep_decimal(), 'mindate' : prep_string(), 'maxdate' : prep_decimal(), 'latitude' : prep_date(), 'name' : prep_string(), 'datacoverage' : prep_decimal(), 'id' : prep_decimal(), 'name' : prep_string(), 'elevationUnit' : prep_decimal(), 'longitude' : prep_decimal() }); get_stations_for_zip()
问题分析与解决方案
出现'DataFrame' object is not callable错误,核心原因是TabPy与Tableau Prep的交互逻辑不匹配,结合代码细节,主要问题和修复点如下:
1. 函数注册逻辑错误
脚本最后直接执行get_stations_for_zip(),会立即返回DataFrame对象,但TabPy需要的是注册可调用的函数服务,而非直接返回数据。Tableau Prep尝试调用这个返回的DataFrame,自然触发“对象不可调用”的错误。
2. get_output_schema存在多处错误
- 重复定义
name列,字典键重复会导致Schema解析异常 - 数据类型映射完全错误:比如
maxdate应为日期类型、id应为字符串类型,错误的类型映射会让Tableau Prep无法识别返回的DataFrame结构
3. 全局变量冗余且易混淆
定义全局dfWorking但函数内又重新赋值局部变量,容易引发变量作用域冲突,导致返回结果不符合预期
修正后的完整代码
import requests import pandas as pd import json from tabpy.tabpy_tools.client import Client # 配置参数 DEFAULT_ZIP = '97034' USER_TOKEN = 'foobar123' HEADERS = {'token': USER_TOKEN} def get_stations_for_zip(zip_input=None): # 支持动态传入邮政编码,增强灵活性 target_zip = zip_input if zip_input is not None else DEFAULT_ZIP api_url = f'https://www.ncei.noaa.gov/cdo-web/api/v2/stations?locationid=ZIP:{target_zip}' response = requests.get(api_url, headers=HEADERS) data = json.loads(response.text) # 容错处理:无结果时返回结构正确的空DataFrame if 'results' not in data: return pd.DataFrame(columns=[ 'elevation', 'mindate', 'maxdate', 'latitude', 'name', 'datacoverage', 'id', 'elevationUnit', 'longitude' ]) results = data['results'] df = pd.DataFrame(results) df['name'] = df['name'].astype(str) return df def get_output_schema(): # 修正数据类型映射,移除重复列 return pd.DataFrame({ 'elevation': prep_decimal(), 'mindate': prep_date(), 'maxdate': prep_date(), 'latitude': prep_decimal(), 'name': prep_string(), 'datacoverage': prep_decimal(), 'id': prep_string(), 'elevationUnit': prep_string(), 'longitude': prep_decimal() }) # 将函数注册到TabPy服务器(根据你的TabPy地址调整端口) client = Client('http://localhost:9004') client.register( 'get_stations_for_zip', get_stations_for_zip, '根据邮政编码返回NOAA气象站列表', schema=get_output_schema() )
关键修复说明
- 移除全局变量:函数内部独立创建并返回DataFrame,避免作用域冲突
- 正确注册函数:使用TabPy Client将函数注册为服务,让Tableau Prep可以正常调用
- 修正Schema类型:确保每个列的
prep_*类型与实际数据匹配,移除重复列 - 增加容错机制:接口无结果时返回结构正确的空DataFrame,避免Prep报错
- 支持动态传参:允许传入自定义邮政编码,适配Tableau Prep的参数调用场景
内容的提问来源于stack exchange,提问作者mxs
相关产品推荐
相关产品推荐

