黙々とデバッグ&コードの整理を行っているわけだが・・・
コードをツギハギで書いていると
from sqlalchemy import func
from sqlalchemy.sql import func
from sqlalchemy.sql.expression import func
と同じfuncを3箇所からimportしていたりする。
これらは別物なのか?別物だったとして、名前は同じで大丈夫なのか?
という疑問が沸々と湧いてきた。
ということでモジュールに含まれている関数を確認する方法をしらべたところ
>>> import sqlalchemy >>> dir(sqlalchemy) ['ARRAY', 'BIGINT', 'BINARY', 'BLANK_SCHEMA', 'BLOB', 'BOOLEAN', 'BigInteger', 'Binary', 'Boolean', 'CHAR', 'CLOB', 'CheckConstraint', 'Column', 'ColumnDefault', 'Constraint', 'DATE', 'DATETIME', 'DDL', 'DECIMAL', 'Date', 'DateTime', 'DefaultClause', 'Enum', 'FLOAT', 'FetchedValue', 'Float', 'ForeignKey', 'ForeignKeyConstraint', 'INT', 'INTEGER', 'Index', 'Integer', 'Interval', 'JSON', 'LargeBinary', 'MetaData', 'NCHAR', 'NUMERIC', 'NVARCHAR', 'Numeric', 'PassiveDefault', 'PickleType', 'PrimaryKeyConstraint', 'REAL', 'SMALLINT', 'Sequence', 'SmallInteger', 'String', 'TEXT', 'TIME', 'TIMESTAMP', 'Table', 'Text', 'ThreadLocalMetaData', 'Time', 'TypeDecorator', 'Unicode', 'UnicodeText', 'UniqueConstraint', 'VARBINARY', 'VARCHAR', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__go', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'alias', 'all_', 'and_', 'any_', 'asc', 'between', 'bindparam', 'case', 'cast', 'collate', 'column', 'cprocessors', 'create_engine', 'cresultproxy', 'cutils', 'delete', 'desc', 'dialects', 'distinct', 'engine', 'engine_from_config', 'event', 'events', 'exc', 'except_', 'except_all', 'exists', 'extract', 'false', 'func', 'funcfilter', 'insert', 'inspect', 'inspection', 'interfaces', 'intersect', 'intersect_all', 'join', 'lateral', 'literal', 'literal_column', 'log', 'modifier', 'not_', 'null', 'or_', 'outerjoin', 'outparam', 'over', 'pool', 'processors', 'schema', 'select', 'sql', 'subquery', 'table', 'tablesample', 'text', 'true', 'tuple_', 'type_coerce', 'types', 'union', 'union_all', 'update', 'util', 'within_group']
このようにdir()で調べることになっているらしい。
で、例えばsqlalchemyにはfuncという関数があるわけだが
>>> dir(sqlalchemy.func) ['_FunctionGenerator__names', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'opts']
一方でsqlalchemy.sqlを調べると
>>> dir(sqlalchemy.sql) ['Alias', 'ClauseElement', 'ClauseVisitor', 'ColumnCollection', 'ColumnElement', 'CompoundSelect', 'Delete', 'False_', 'FromClause', 'Insert', 'Join', 'Select', 'Selectable', 'TableClause', 'TableSample', 'True_', 'Update', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__go', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'alias', 'all_', 'and_', 'annotation', 'any_', 'asc', 'base', 'between', 'bindparam', 'case', 'cast', 'collate', 'column', 'compiler', 'crud', 'ddl', 'default_comparator', 'delete', 'desc', 'distinct', 'dml', 'elements', 'except_', 'except_all', 'exists', 'expression', 'extract', 'false', 'func', 'funcfilter', 'functions', 'insert', 'intersect', 'intersect_all', 'join', 'label', 'lateral', 'literal', 'literal_column', 'modifier', 'naming', 'not_', 'null', 'operators', 'or_', 'outerjoin', 'outparam', 'over', 'schema', 'select', 'selectable', 'sqltypes', 'subquery', 'table', 'tablesample', 'text', 'true', 'tuple_', 'type_api', 'type_coerce', 'union', 'union_all', 'update', 'util', 'visitors', 'within_group']
こうなっており、たしかにここにもfuncがある。なのでsqlalchemy.sql.funcも調べてみると
>>> dir(sqlalchemy.sql.func) ['_FunctionGenerator__names', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'opts']
このように、どうやらsqlalchemy.funcと同じ内容になっているのではないかと予想される。
ちなみにsqlalchemy.sql.expression.funcも
>>> dir(sqlalchemy.sql.expression.func) ['_FunctionGenerator__names', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'opts']
と同一っぽいので、これらはどれか一つをインポートしておけば良さそうな気がするわけだ。
念のためmoduleを直接調べてみる。
>>> import sqlalchemy >>> print(sqlalchemy.__file__) /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/__init__.py
sqlalchemyのありかをこのように調べて、lsで覗いてみると、まんまpythonのプログラムなんで
__init__.py ext __pycache__ inspection.py connectors interfaces.py cprocessors.cpython-36m-darwin.so log.py cresultproxy.cpython-36m-darwin.so orm cutils.cpython-36m-darwin.so pool.py databases processors.py dialects schema.py engine sql event testing events.py types.py exc.py util
こんな感じに構成がわかる。ここを見るとsqlはあるがfuncは見当たらない。
で、__init__.pyをlessすると
# sqlalchemy/__init__.py # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors # <see AUTHORS file> # # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php from .sql import ( alias, all_, and_, any_, asc, between, bindparam, case, cast, collate, column, delete, desc, distinct, except_, except_all, exists, extract, false, func, funcfilter, insert, intersect, intersect_all, join, lateral, :
こんな感じにsqlからいろいろインポートしており、そこにfuncも存在している。でもってfuncはsql/functions.pyの中で定義されていることがわかった。要するに同じものを読み込んでいると考えて間違いなさそうだな。
sqlalchemyをimportする際に個別の関数だけをimportせずにまるごとimportして使うことが想定されているのかもしれない。ややこしいな。結局同じモジュール内の同名の関数は1つだけimportしておけばいいということで間違いなさそうだ。