是否可以找到给定 class 变量的 class 名称
is it possible to find class name given class variable
class A:
var = 'hello'
type(A.var) # returns: <class 'str'>
A.var.__class__.__name__ # returns: 'str'
vars(A) # returns: mappingproxy({'__module__': '__main__', 'var': 'hello', '__dict__': <attribute ' __dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, '__getattribute__': <slot wrapper '__getattribute__' of 'object' objects>})
给定 A.var
是否有可能找到 class A,或者只是 var 与 class A 相关,可能类似于 'qualname' 用于方法。
已编辑
我想找到 class 名称的原因是我正在实现微型 ORM,类似于使用 declarative mapping.
的 SQLAlchemy
如您在此示例中所见。
import sqlalchemy as sa
import sqlalchemy.ext.declarative
meta = sa.MetaData()
DeclarativeBase = sa.ext.declarative.declarative_base(metadata=meta)
class User(DeclartiveBase):
__tablename__ = 'user'
id = sa.Column(Integer, primary_key=True)
name = sa.Column(String)
fullname = sa.Column(String)
如您所见,class 用作真实 table 的抽象,有趣的是您仍然可以使用 vars(Users)
访问所有列并使用 [= 过滤列20=].
在进行查询时,查询如何能够知道该行来自哪个 table,这与我上面提出的问题类似,可能与 sa.Column.[= 有关24=]
Session = sa.orm.sessionmaker(bind=engine)
session = Session()
rows = session.query(
User.id,
User.name,
).all()
样本
这是实现的示例,它能够进行基本的 table 创建、插入和更新。
class Col(str):
pass
class Base:
@classmethod
def childs(cls):
subclasses = set()
work = [cls]
while work:
parent = work.pop()
for child in parent.__subclasses__():
if child not in subclasses:
subclasses.add(child)
work.append(child)
return subclasses
@classmethod
def create(cls, cursor):
query = 'CREATE TABLE IF NOT EXISTS {} ({})'.format(
cls.__tablename__,
', '.join(
k + ' ' + v
for k, v in vars(cls).items()
if isinstance(v, Col)
)
)
cursor.execute(query)
@staticmethod
def create_all_tables(cursor):
for cls in __class__.childs():
cls.create(cursor)
@classmethod
def drop(cls, cursor):
query = 'DROP TABLE IF EXISTS {}'.format(
cls.__tablename__,
)
cursor.execute(query)
@classmethod
def insert(cls, cursor, kvRow):
query = 'INSERT INTO {} ({}) VALUES(:{})'.format(
cls.__tablename__,
', '.join(kvRow.keys()),
', :'.join(kvRow.keys())
)
cursor.execute(query, kvRow)
使用它
class User(Base):
__tablename__ = 'user'
id = Col('INTEGER PRIMARY KEY')
name = Col('VARCHAR')
fullname = Col('VARCHAR')
现在创建 table 类似于 sqlalchemy meta.create_all(bind=engine, checkfirst=True)
import sqlite3
conn = sqlite3.connect('./sqlite.db')
cursor = conn.cursor()
Base.create_all_tables(cursor)
我不明白 sqlchemy session.query
是如何理解 table 该行的来源。
嗯,不可能将 class A 关联到变量,因为您正在创建一个 class A,但实例化了一个与该变量无关的字符串 class.
如果您创建 class 并关联一个属性,而不是仅在 class 中创建原始字符串,那么您可以 return class.
class A:
def __init__(self, value):
self.__value = value
def __str__(self):
return self.__value
def __eq__(self, other):
return self.__value == other
def retunclass(self):
return self.__class__.__name__
var = A('hello') #var = 'hello'
print(var.retunclass()) #>>> 'A'
if var == 'hello':
print(True) #>>> True
如果我对你的问题理解正确的话,你处理的主要问题是:如何创建一个对象,当它被定义为一个class作为它的属性时,"知道”class它属于什么?
为了实现这一点,SQLAlchemy 使用 metaclasses(高级主题...),但也有更简单的技术,尤其是在现代版本的 Python。其中之一,我认为对你来说特别方便,是让你的 Col
class 实现 描述符协议的特定部分 .
一个简单的方法——基于__set_name__
参考Python docs,当涉及到您的用例时,描述符协议的相关部分是方法:
__set_name__(self, owner, name)
Called at the time the owning class owner is created. The descriptor has been assigned to name.
例如,您可以按如下方式实现此方法:
class Col(str):
owner = None
name = None
def __set_name__(self, owner, name):
self.owner = owner
self.name = name
然后,如果您在 class 定义中放置一些 Col
的实例作为 class 的属性,他们将通过调用他们的 __set_name__
,就在创建 class 之后。多亏了这一点,他们每个人都会“知道” class 和作为 class 的属性获得的名称:
>>> class ArbitraryClass:
... id = Col('INTEGER PRIMARY KEY')
... name = Col('VARCHAR')
... fullname = Col('VARCHAR')
...
>>> ArbitraryClass.id
'INTEGER PRIMARY KEY'
>>> ArbitraryClass.id.owner
<class '__main__.ArbitraryClass'>
>>> ArbitraryClass.id.name
'id'
>>> ArbitraryClass.name.owner
<class '__main__.ArbitraryClass'>
>>> ArbitraryClass.name.name
'name'
>>> ArbitraryClass.fullname.owner
<class '__main__.ArbitraryClass'>
>>> ArbitraryClass.fullname.name
'fullname'
另一种方法 - 基于 __init_subclass__
# We keep `Col` very simple:
class Col(str):
owner = None
name = None
# ...as the responsibility of detecting `Col` attributes
# is taken by the `Base` class:
class Base:
def __init_subclass__(cls, /, **kwargs):
super().__init_subclass__(**kwargs)
for name, obj in vars(cls).items():
if isinstance(obj, Col):
obj.owner = cls
obj.name = name
# And below the rest of your implementation of `Base`...
上述定义的使用示例:
>>> class Table(Base):
... id = Col('INTEGER PRIMARY KEY')
... name = Col('VARCHAR')
... fullname = Col('VARCHAR')
...
>>> Table.id
'INTEGER PRIMARY KEY'
>>> Table.id.owner
<class '__main__.Table'>
>>> Table.id.name
'id'
>>> Table.name.owner
<class '__main__.Table'>
>>> Table.name.name
'name'
>>> Table.fullname.owner
<class '__main__.Table'>
>>> Table.fullname.name
'fullname'
另一种替代方法 - metaclass-based
另一种可能性是使用 metaclass —— 其方式与我们在上面对 __init_subclass__
所做的非常相似。
# Again, we keep `Col` very simple:
class Col(str):
owner = None
name = None
# ...because a metaclass will do the work:
class BaseMeta(type):
def __new__(metacls, name, bases, namespace, **kwargs):
cls = super().__new__(metacls, name, bases, namespace, **kwargs)
for name, obj in vars(cls).items():
if isinstance(obj, Col):
obj.owner = cls
obj.name = name
return cls
class Base(metaclass=BaseMeta):
# Note: ↑ by passing `metaclass=BaseMeta` we say:
# the class of the `Base` class is `BaseMeta`.
# That's the essence: *metaclass* == *class of a class*.
# And below the rest of your implementation of `Base`...
用法与基于 __init_subclass__()
的方法相同...
注意:metaclasses 是一个非常强大的工具,因为您可以使用它们自定义 class 创建过程的各个阶段。然而,在这里,我认为让他们参与会有点矫枉过正。此外,当您需要多个 metaclass 提供的功能时,使用此技术会变得很麻烦。编写不同的现有元classes 可能会变得困难甚至不可能。
所以我宁愿建议前面描述的一种更简单的方法。
如果要查找查询中的列“属于”的模型 class,可以使用查询的 column_descriptions 属性:
q = session.query(User.id, User.name)
q.column_descriptions
产出
[{
'name': 'id',
'type': Integer(),
'aliased': False,
'expr': <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7fa18b87dcc0>,
'entity': <class '__main__.User'>
},
{
'name': 'name',
'type': String(),
'aliased': False,
'expr': <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7fa18b87dd60>,
'entity': <class '__main__.User'>
}]
因此可以在每列的 entity
键的值中找到模型 class。这适用于 1.x session.query
语法和 2.0 sa.select(User.id, User.name)
语法。
class A:
var = 'hello'
type(A.var) # returns: <class 'str'>
A.var.__class__.__name__ # returns: 'str'
vars(A) # returns: mappingproxy({'__module__': '__main__', 'var': 'hello', '__dict__': <attribute ' __dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, '__getattribute__': <slot wrapper '__getattribute__' of 'object' objects>})
给定 A.var
是否有可能找到 class A,或者只是 var 与 class A 相关,可能类似于 'qualname' 用于方法。
已编辑
我想找到 class 名称的原因是我正在实现微型 ORM,类似于使用 declarative mapping.
的 SQLAlchemy如您在此示例中所见。
import sqlalchemy as sa
import sqlalchemy.ext.declarative
meta = sa.MetaData()
DeclarativeBase = sa.ext.declarative.declarative_base(metadata=meta)
class User(DeclartiveBase):
__tablename__ = 'user'
id = sa.Column(Integer, primary_key=True)
name = sa.Column(String)
fullname = sa.Column(String)
如您所见,class 用作真实 table 的抽象,有趣的是您仍然可以使用 vars(Users)
访问所有列并使用 [= 过滤列20=].
在进行查询时,查询如何能够知道该行来自哪个 table,这与我上面提出的问题类似,可能与 sa.Column.[= 有关24=]
Session = sa.orm.sessionmaker(bind=engine)
session = Session()
rows = session.query(
User.id,
User.name,
).all()
样本
这是实现的示例,它能够进行基本的 table 创建、插入和更新。
class Col(str):
pass
class Base:
@classmethod
def childs(cls):
subclasses = set()
work = [cls]
while work:
parent = work.pop()
for child in parent.__subclasses__():
if child not in subclasses:
subclasses.add(child)
work.append(child)
return subclasses
@classmethod
def create(cls, cursor):
query = 'CREATE TABLE IF NOT EXISTS {} ({})'.format(
cls.__tablename__,
', '.join(
k + ' ' + v
for k, v in vars(cls).items()
if isinstance(v, Col)
)
)
cursor.execute(query)
@staticmethod
def create_all_tables(cursor):
for cls in __class__.childs():
cls.create(cursor)
@classmethod
def drop(cls, cursor):
query = 'DROP TABLE IF EXISTS {}'.format(
cls.__tablename__,
)
cursor.execute(query)
@classmethod
def insert(cls, cursor, kvRow):
query = 'INSERT INTO {} ({}) VALUES(:{})'.format(
cls.__tablename__,
', '.join(kvRow.keys()),
', :'.join(kvRow.keys())
)
cursor.execute(query, kvRow)
使用它
class User(Base):
__tablename__ = 'user'
id = Col('INTEGER PRIMARY KEY')
name = Col('VARCHAR')
fullname = Col('VARCHAR')
现在创建 table 类似于 sqlalchemy meta.create_all(bind=engine, checkfirst=True)
import sqlite3
conn = sqlite3.connect('./sqlite.db')
cursor = conn.cursor()
Base.create_all_tables(cursor)
我不明白 sqlchemy session.query
是如何理解 table 该行的来源。
嗯,不可能将 class A 关联到变量,因为您正在创建一个 class A,但实例化了一个与该变量无关的字符串 class. 如果您创建 class 并关联一个属性,而不是仅在 class 中创建原始字符串,那么您可以 return class.
class A:
def __init__(self, value):
self.__value = value
def __str__(self):
return self.__value
def __eq__(self, other):
return self.__value == other
def retunclass(self):
return self.__class__.__name__
var = A('hello') #var = 'hello'
print(var.retunclass()) #>>> 'A'
if var == 'hello':
print(True) #>>> True
如果我对你的问题理解正确的话,你处理的主要问题是:如何创建一个对象,当它被定义为一个class作为它的属性时,"知道”class它属于什么?
为了实现这一点,SQLAlchemy 使用 metaclasses(高级主题...),但也有更简单的技术,尤其是在现代版本的 Python。其中之一,我认为对你来说特别方便,是让你的 Col
class 实现 描述符协议的特定部分 .
一个简单的方法——基于__set_name__
参考Python docs,当涉及到您的用例时,描述符协议的相关部分是方法:
__set_name__(self, owner, name)
Called at the time the owning class owner is created. The descriptor has been assigned to name.
例如,您可以按如下方式实现此方法:
class Col(str):
owner = None
name = None
def __set_name__(self, owner, name):
self.owner = owner
self.name = name
然后,如果您在 class 定义中放置一些 Col
的实例作为 class 的属性,他们将通过调用他们的 __set_name__
,就在创建 class 之后。多亏了这一点,他们每个人都会“知道” class 和作为 class 的属性获得的名称:
>>> class ArbitraryClass:
... id = Col('INTEGER PRIMARY KEY')
... name = Col('VARCHAR')
... fullname = Col('VARCHAR')
...
>>> ArbitraryClass.id
'INTEGER PRIMARY KEY'
>>> ArbitraryClass.id.owner
<class '__main__.ArbitraryClass'>
>>> ArbitraryClass.id.name
'id'
>>> ArbitraryClass.name.owner
<class '__main__.ArbitraryClass'>
>>> ArbitraryClass.name.name
'name'
>>> ArbitraryClass.fullname.owner
<class '__main__.ArbitraryClass'>
>>> ArbitraryClass.fullname.name
'fullname'
另一种方法 - 基于 __init_subclass__
# We keep `Col` very simple:
class Col(str):
owner = None
name = None
# ...as the responsibility of detecting `Col` attributes
# is taken by the `Base` class:
class Base:
def __init_subclass__(cls, /, **kwargs):
super().__init_subclass__(**kwargs)
for name, obj in vars(cls).items():
if isinstance(obj, Col):
obj.owner = cls
obj.name = name
# And below the rest of your implementation of `Base`...
上述定义的使用示例:
>>> class Table(Base):
... id = Col('INTEGER PRIMARY KEY')
... name = Col('VARCHAR')
... fullname = Col('VARCHAR')
...
>>> Table.id
'INTEGER PRIMARY KEY'
>>> Table.id.owner
<class '__main__.Table'>
>>> Table.id.name
'id'
>>> Table.name.owner
<class '__main__.Table'>
>>> Table.name.name
'name'
>>> Table.fullname.owner
<class '__main__.Table'>
>>> Table.fullname.name
'fullname'
另一种替代方法 - metaclass-based
另一种可能性是使用 metaclass —— 其方式与我们在上面对 __init_subclass__
所做的非常相似。
# Again, we keep `Col` very simple:
class Col(str):
owner = None
name = None
# ...because a metaclass will do the work:
class BaseMeta(type):
def __new__(metacls, name, bases, namespace, **kwargs):
cls = super().__new__(metacls, name, bases, namespace, **kwargs)
for name, obj in vars(cls).items():
if isinstance(obj, Col):
obj.owner = cls
obj.name = name
return cls
class Base(metaclass=BaseMeta):
# Note: ↑ by passing `metaclass=BaseMeta` we say:
# the class of the `Base` class is `BaseMeta`.
# That's the essence: *metaclass* == *class of a class*.
# And below the rest of your implementation of `Base`...
用法与基于 __init_subclass__()
的方法相同...
注意:metaclasses 是一个非常强大的工具,因为您可以使用它们自定义 class 创建过程的各个阶段。然而,在这里,我认为让他们参与会有点矫枉过正。此外,当您需要多个 metaclass 提供的功能时,使用此技术会变得很麻烦。编写不同的现有元classes 可能会变得困难甚至不可能。
所以我宁愿建议前面描述的一种更简单的方法。
如果要查找查询中的列“属于”的模型 class,可以使用查询的 column_descriptions 属性:
q = session.query(User.id, User.name)
q.column_descriptions
产出
[{
'name': 'id',
'type': Integer(),
'aliased': False,
'expr': <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7fa18b87dcc0>,
'entity': <class '__main__.User'>
},
{
'name': 'name',
'type': String(),
'aliased': False,
'expr': <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7fa18b87dd60>,
'entity': <class '__main__.User'>
}]
因此可以在每列的 entity
键的值中找到模型 class。这适用于 1.x session.query
语法和 2.0 sa.select(User.id, User.name)
语法。