<p dir="auto"><strong>Migrated issue, originally created by Michael Bayer (<a href="https://github.com/zzzeek">@zzzeek</a>)</strong></p>
<p dir="auto">use case:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, declared_attr, cascading_declared_attr

Base = declarative_base()

class HasId(object):
    @cascading_declared_attr
    def id(cls):
        if cls.__name__=='Content':
            return Column('id', Integer, primary_key=True)
        else:
            return Column('id', Integer, ForeignKey('content.id'), primary_key=True)

class Content(HasId, Base):

    content_type = Column(String(20))

    @cascading_declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

class Project(Content):
    currency  = Column(String(3))
    target = Column(Integer)
    current = Column(Integer)



#!diff
diff --git a/lib/sqlalchemy/ext/declarative/__init__.py b/lib/sqlalchemy/ext/declarative/__init__.py
index 0ee4e33..4566324 100644
--- a/lib/sqlalchemy/ext/declarative/__init__.py
+++ b/lib/sqlalchemy/ext/declarative/__init__.py
@@ -1300,7 +1300,7 @@ Mapped instances then make usage of
 from .api import declarative_base, synonym_for, comparable_using, \
     instrument_declarative, ConcreteBase, AbstractConcreteBase, \
     DeclarativeMeta, DeferredReflection, has_inherited_table,\
-    declared_attr, as_declarative
+    declared_attr, cascading_declared_attr, as_declarative
 
 
 __all__ = ['synonym_for', 'has_inherited_table',
diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py
index 2418c6e..dc6c42b 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -162,6 +162,26 @@ class declared_attr(interfaces._MappedAttribute, property):
     def __get__(desc, self, cls):
         return desc.fget(cls)
 
+class cascading_declared_attr(declared_attr):
+    &quot;&quot;&quot;A :class:`.declared_attr` that will be invoked for all subclasses.
+
+    Use :class:`.cascading_declared_attr` when a particular ``@declared_attr``
+    needs to be invoked individually for each subclass in a hierarchy::
+
+        class HasId(object):
+            @cascading_declared_attr
+            def id(cls):
+                if has_inherited_table(cls):
+                    return Column(Integer, ForeignKey(&quot;content.id&quot;), primary_key=True)
+                else:
+                    return Column(Integer, primary_key=True)
+
+        class Content(HasId, Base):
+            pass
+
+        class SubContent(Content):
+            pass
+    &quot;&quot;&quot;
 
 def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
                      name='Base', constructor=_declarative_constructor,
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 4fda9c7..a8702cc 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -31,7 +31,7 @@ def _declared_mapping_info(cls):
 
 
 def _as_declarative(cls, classname, dict_):
-    from .api import declared_attr
+    from .api import declared_attr, cascading_declared_attr
 
     # dict_ will be a dictproxy, which we can't write to, and we need to!
     dict_ = dict(dict_)
@@ -96,6 +96,12 @@ def _as_declarative(cls, classname, dict_):
                             &quot;not applying to subclass %s.&quot;
                             % (base.__name__, name, base, cls))
                 continue
+            elif isinstance(obj, cascading_declared_attr):
+                ret = obj.__get__(obj, cls)
+                dict_[name] = column_copies[obj] = ret
+                if isinstance(ret, (Column, MapperProperty)) and \
+                    ret.doc is None:
+                    ret.doc = obj.__doc__
             elif base is not cls:
                 # we're a mixin.
                 if isinstance(obj, Column):
@@ -125,8 +131,8 @@ def _as_declarative(cls, classname, dict_):
                         &quot;be declared as @declared_attr callables &quot;
                         &quot;on declarative mixin classes.&quot;)
                 elif isinstance(obj, declarative_props):
-                    dict_[name](name) = ret = \
-                            column_copies[obj] = getattr(cls, name)
+                    ret = getattr(cls, name)
+                    dict_[name] = column_copies[obj] = ret
                     if isinstance(ret, (Column, MapperProperty)) and \
                         ret.doc is None:
                         ret.doc = obj.__doc__"><pre class="notranslate"><code class="notranslate">from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, declared_attr, cascading_declared_attr

Base = declarative_base()

class HasId(object):
    @cascading_declared_attr
    def id(cls):
        if cls.__name__=='Content':
            return Column('id', Integer, primary_key=True)
        else:
            return Column('id', Integer, ForeignKey('content.id'), primary_key=True)

class Content(HasId, Base):

    content_type = Column(String(20))

    @cascading_declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

class Project(Content):
    currency  = Column(String(3))
    target = Column(Integer)
    current = Column(Integer)



#!diff
diff --git a/lib/sqlalchemy/ext/declarative/__init__.py b/lib/sqlalchemy/ext/declarative/__init__.py
index 0ee4e33..4566324 100644
--- a/lib/sqlalchemy/ext/declarative/__init__.py
+++ b/lib/sqlalchemy/ext/declarative/__init__.py
@@ -1300,7 +1300,7 @@ Mapped instances then make usage of
 from .api import declarative_base, synonym_for, comparable_using, \
     instrument_declarative, ConcreteBase, AbstractConcreteBase, \
     DeclarativeMeta, DeferredReflection, has_inherited_table,\
-    declared_attr, as_declarative
+    declared_attr, cascading_declared_attr, as_declarative
 
 
 __all__ = ['synonym_for', 'has_inherited_table',
diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py
index 2418c6e..dc6c42b 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -162,6 +162,26 @@ class declared_attr(interfaces._MappedAttribute, property):
     def __get__(desc, self, cls):
         return desc.fget(cls)
 
+class cascading_declared_attr(declared_attr):
+    """A :class:`.declared_attr` that will be invoked for all subclasses.
+
+    Use :class:`.cascading_declared_attr` when a particular ``@declared_attr``
+    needs to be invoked individually for each subclass in a hierarchy::
+
+        class HasId(object):
+            @cascading_declared_attr
+            def id(cls):
+                if has_inherited_table(cls):
+                    return Column(Integer, ForeignKey("content.id"), primary_key=True)
+                else:
+                    return Column(Integer, primary_key=True)
+
+        class Content(HasId, Base):
+            pass
+
+        class SubContent(Content):
+            pass
+    """
 
 def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
                      name='Base', constructor=_declarative_constructor,
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 4fda9c7..a8702cc 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -31,7 +31,7 @@ def _declared_mapping_info(cls):
 
 
 def _as_declarative(cls, classname, dict_):
-    from .api import declared_attr
+    from .api import declared_attr, cascading_declared_attr
 
     # dict_ will be a dictproxy, which we can't write to, and we need to!
     dict_ = dict(dict_)
@@ -96,6 +96,12 @@ def _as_declarative(cls, classname, dict_):
                             "not applying to subclass %s."
                             % (base.__name__, name, base, cls))
                 continue
+            elif isinstance(obj, cascading_declared_attr):
+                ret = obj.__get__(obj, cls)
+                dict_[name] = column_copies[obj] = ret
+                if isinstance(ret, (Column, MapperProperty)) and \
+                    ret.doc is None:
+                    ret.doc = obj.__doc__
             elif base is not cls:
                 # we're a mixin.
                 if isinstance(obj, Column):
@@ -125,8 +131,8 @@ def _as_declarative(cls, classname, dict_):
                         "be declared as @declared_attr callables "
                         "on declarative mixin classes.")
                 elif isinstance(obj, declarative_props):
-                    dict_[name](name) = ret = \
-                            column_copies[obj] = getattr(cls, name)
+                    ret = getattr(cls, name)
+                    dict_[name] = column_copies[obj] = ret
                     if isinstance(ret, (Column, MapperProperty)) and \
                         ret.doc is None:
                         ret.doc = obj.__doc__
</code></pre></div>
<p dir="auto"><strong>Migrated issue, originally created by Michael Bayer (<a href="https://github.com/zzzeek">@zzzeek</a>)</strong></p>
<p dir="auto">poc should allow us to consolidate <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="384627265" data-permission-text="Title is private" data-url="https://github.com/sqlalchemy/sqlalchemy/issues/2670" data-hovercard-type="issue" data-hovercard-url="/sqlalchemy/sqlalchemy/issues/2670/hovercard" href="https://github.com/sqlalchemy/sqlalchemy/issues/2670">#2670</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="384629557" data-permission-text="Title is private" data-url="https://github.com/sqlalchemy/sqlalchemy/issues/2952" data-hovercard-type="issue" data-hovercard-url="/sqlalchemy/sqlalchemy/issues/2952/hovercard" href="https://github.com/sqlalchemy/sqlalchemy/issues/2952">#2952</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="384631430" data-permission-text="Title is private" data-url="https://github.com/sqlalchemy/sqlalchemy/issues/3149" data-hovercard-type="issue" data-hovercard-url="/sqlalchemy/sqlalchemy/issues/3149/hovercard" href="https://github.com/sqlalchemy/sqlalchemy/issues/3149">#3149</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="384630495" data-permission-text="Title is private" data-url="https://github.com/sqlalchemy/sqlalchemy/issues/3050" data-hovercard-type="issue" data-hovercard-url="/sqlalchemy/sqlalchemy/issues/3050/hovercard" href="https://github.com/sqlalchemy/sqlalchemy/issues/3050">#3050</a>.</p>
<p dir="auto">the new descriptors include the ability to cache the result per class, or to do "cascade", guarantees that the callable fn is called only once per target class, as well as to name attributes that are set up after the mapping is complete, so that relationship and column_property declared_attrs have the whole mapping to work with when they are called.  and the whole thing doesn't modify any existing functionality, only adds new things we can take time to stabilize.  win win win win.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, declared_attr, has_inherited_table

Base = declarative_base()


class VehicleModel(Base):
    __tablename__ = &quot;vehicle_model&quot;
    id = Column(Integer, primary_key=True)
    name = Column(String(20))


class VehicleInfo(object):
    vehicle_plate_region = Column(String(5))
    vehicle_plate = Column(String(20))

    @declared_attr.column
    def vehicle_model_id(cls):
        return Column(Integer, ForeignKey(&quot;vehicle_model.id&quot;))

    @declared_attr.property
    def vehicle_model(cls):
        # 1. called after the class is fully mapped
        # 2. called only once for each class

        assert cls.__table__ is not None and \
            cls.__table__.c.vehicle_model_id.shares_lineage(
                cls.vehicle_model_id.__clause_element__()
            )

        return relationship(VehicleModel, foreign_keys=[cls.vehicle_model_id])

    @declared_attr.column.cascading
    def id(cls):
        if has_inherited_table(cls):
            return Column(Integer, ForeignKey(&quot;vehicle.id&quot;), primary_key=True)
        else:
            return Column(Integer, primary_key=True)


class Vehicle(VehicleInfo, Base):
    __tablename__ = 'vehicle'


class SubVehicle(Vehicle):
    __tablename__ = 'subveh'

    @declared_attr.property
    def some_other_thing(cls):
        # called way at the end
        assert cls.id.__clause_element__().references(Vehicle.__table__.c.id)
        return column_property(cls.id)

configure_mappers()

"><pre class="notranslate"><code class="notranslate">from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, declared_attr, has_inherited_table

Base = declarative_base()


class VehicleModel(Base):
    __tablename__ = "vehicle_model"
    id = Column(Integer, primary_key=True)
    name = Column(String(20))


class VehicleInfo(object):
    vehicle_plate_region = Column(String(5))
    vehicle_plate = Column(String(20))

    @declared_attr.column
    def vehicle_model_id(cls):
        return Column(Integer, ForeignKey("vehicle_model.id"))

    @declared_attr.property
    def vehicle_model(cls):
        # 1. called after the class is fully mapped
        # 2. called only once for each class

        assert cls.__table__ is not None and \
            cls.__table__.c.vehicle_model_id.shares_lineage(
                cls.vehicle_model_id.__clause_element__()
            )

        return relationship(VehicleModel, foreign_keys=[cls.vehicle_model_id])

    @declared_attr.column.cascading
    def id(cls):
        if has_inherited_table(cls):
            return Column(Integer, ForeignKey("vehicle.id"), primary_key=True)
        else:
            return Column(Integer, primary_key=True)


class Vehicle(VehicleInfo, Base):
    __tablename__ = 'vehicle'


class SubVehicle(Vehicle):
    __tablename__ = 'subveh'

    @declared_attr.property
    def some_other_thing(cls):
        # called way at the end
        assert cls.id.__clause_element__().references(Vehicle.__table__.c.id)
        return column_property(cls.id)

configure_mappers()


</code></pre></div>
<p dir="auto">poc patch, however still needs integration for <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="384627265" data-permission-text="Title is private" data-url="https://github.com/sqlalchemy/sqlalchemy/issues/2670" data-hovercard-type="issue" data-hovercard-url="/sqlalchemy/sqlalchemy/issues/2670/hovercard" href="https://github.com/sqlalchemy/sqlalchemy/issues/2670">#2670</a>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py
index daf8bff..251b06f 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -13,7 +13,7 @@ from ...orm import synonym as _orm_synonym, mapper,\
     interfaces, properties
 from ...orm.util import polymorphic_union
 from ...orm.base import _mapper_or_none
-from ...util import OrderedDict
+from ...util import OrderedDict, classproperty
 from ... import exc
 import weakref
 
@@ -164,6 +164,45 @@ class declared_attr(interfaces._MappedAttribute, property):
     def __get__(desc, self, cls):
         return desc.fget(cls)
 
+    @classproperty
+    def column(cls):
+        return _declared_column
+
+    @classproperty
+    def property(cls):
+        return _declared_property
+
+    defer_defer_defer = False
+
+
+class _memoized_declared_attr(declared_attr):
+    def __init__(self, fget, cascading=False):
+        super(_memoized_declared_attr, self).__init__(fget)
+        self.reg = weakref.WeakKeyDictionary()
+        self._cascading = cascading
+
+    def __get__(desc, self, cls):
+        if desc.defer_defer_defer:
+            return desc
+        elif cls in desc.reg:
+            return desc.reg[cls]
+        else:
+            desc.reg[cls] = obj = desc.fget(cls)
+            return obj
+
+    @classproperty
+    def cascading(cls):
+        return lambda decorated: cls(decorated, cascading=True)
+
+
+class _declared_column(_memoized_declared_attr):
+    pass
+
+
+class _declared_property(_memoized_declared_attr):
+    defer_defer_defer = True
+
+
 
 def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
                      name='Base', constructor=_declarative_constructor,
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 94baeeb..cee2263 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -33,7 +33,7 @@ def _declared_mapping_info(cls):
 
 
 def _as_declarative(cls, classname, dict_):
-    from .api import declared_attr
+    from .api import declared_attr, _memoized_declared_attr
 
     # dict_ will be a dictproxy, which we can't write to, and we need to!
     dict_ = dict(dict_)
@@ -132,6 +132,14 @@ def _as_declarative(cls, classname, dict_):
                         &quot;column_property(), relationship(), etc.) must &quot;
                         &quot;be declared as @declared_attr callables &quot;
                         &quot;on declarative mixin classes.&quot;)
+                elif isinstance(obj, _memoized_declared_attr): # and \
+                    if obj._cascading:
+                        dict_[name] = ret = obj.__get__(obj, cls)
+                    else:
+                        dict_[name] = ret = getattr(cls, name)
+                    if isinstance(ret, (Column, MapperProperty)) and \
+                            ret.doc is None:
+                        ret.doc = obj.__doc__
                 elif isinstance(obj, declarative_props):
                     dict_[name] = ret = \
                         column_copies[obj] = getattr(cls, name)
@@ -148,6 +156,7 @@ def _as_declarative(cls, classname, dict_):
 
     clsregistry.add_class(classname, cls)
     our_stuff = util.OrderedDict()
+    add_later = util.OrderedDict()
 
     for k in list(dict_):
 
@@ -157,7 +166,10 @@ def _as_declarative(cls, classname, dict_):
 
         value = dict_[k]
         if isinstance(value, declarative_props):
-            value = getattr(cls, k)
+            if value.defer_defer_defer:
+                add_later[k] = value
+            else:
+                value = getattr(cls, k)
 
         elif isinstance(value, QueryableAttribute) and \
                 value.class_ is not cls and \
@@ -324,7 +336,8 @@ def _as_declarative(cls, classname, dict_):
                  declared_columns,
                  column_copies,
                  our_stuff,
-                 mapper_args_fn)
+                 mapper_args_fn,
+                 add_later)
     if not defer_map:
         mt.map()
 
@@ -339,7 +352,8 @@ class _MapperConfig(object):
                  inherits,
                  declared_columns,
                  column_copies,
-                 properties, mapper_args_fn):
+                 properties, mapper_args_fn,
+                 add_later):
         self.mapper_cls = mapper_cls
         self.cls = cls
         self.local_table = table
@@ -348,6 +362,7 @@ class _MapperConfig(object):
         self.mapper_args_fn = mapper_args_fn
         self.declared_columns = declared_columns
         self.column_copies = column_copies
+        self.add_later = add_later
 
     def _prepare_mapper_arguments(self):
         properties = self.properties
@@ -410,6 +425,8 @@ class _MapperConfig(object):
             self.local_table,
             **mapper_args
         )
+        for k, v in self.add_later.items():
+            setattr(self.cls, k, v.fget(self.cls))
 
 
 class _DeferredMapperConfig(_MapperConfig):
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index f3af46c..ea8c32f 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -1161,8 +1161,10 @@ class Column(SchemaItem, ColumnClause):
         existing = getattr(self, 'table', None)
         if existing is not None and existing is not table:
             raise exc.ArgumentError(
-                &quot;Column object already assigned to Table '%s'&quot; %
-                existing.description)
+                &quot;Column object '%s' already assigned to Table '%s'&quot; % (
+                    self.key,
+                    existing.description
+                ))
 
         if self.key in table._columns:
             col = table._columns.get(self.key)"><pre class="notranslate"><code class="notranslate">diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py
index daf8bff..251b06f 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -13,7 +13,7 @@ from ...orm import synonym as _orm_synonym, mapper,\
     interfaces, properties
 from ...orm.util import polymorphic_union
 from ...orm.base import _mapper_or_none
-from ...util import OrderedDict
+from ...util import OrderedDict, classproperty
 from ... import exc
 import weakref
 
@@ -164,6 +164,45 @@ class declared_attr(interfaces._MappedAttribute, property):
     def __get__(desc, self, cls):
         return desc.fget(cls)
 
+    @classproperty
+    def column(cls):
+        return _declared_column
+
+    @classproperty
+    def property(cls):
+        return _declared_property
+
+    defer_defer_defer = False
+
+
+class _memoized_declared_attr(declared_attr):
+    def __init__(self, fget, cascading=False):
+        super(_memoized_declared_attr, self).__init__(fget)
+        self.reg = weakref.WeakKeyDictionary()
+        self._cascading = cascading
+
+    def __get__(desc, self, cls):
+        if desc.defer_defer_defer:
+            return desc
+        elif cls in desc.reg:
+            return desc.reg[cls]
+        else:
+            desc.reg[cls] = obj = desc.fget(cls)
+            return obj
+
+    @classproperty
+    def cascading(cls):
+        return lambda decorated: cls(decorated, cascading=True)
+
+
+class _declared_column(_memoized_declared_attr):
+    pass
+
+
+class _declared_property(_memoized_declared_attr):
+    defer_defer_defer = True
+
+
 
 def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
                      name='Base', constructor=_declarative_constructor,
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 94baeeb..cee2263 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -33,7 +33,7 @@ def _declared_mapping_info(cls):
 
 
 def _as_declarative(cls, classname, dict_):
-    from .api import declared_attr
+    from .api import declared_attr, _memoized_declared_attr
 
     # dict_ will be a dictproxy, which we can't write to, and we need to!
     dict_ = dict(dict_)
@@ -132,6 +132,14 @@ def _as_declarative(cls, classname, dict_):
                         "column_property(), relationship(), etc.) must "
                         "be declared as @declared_attr callables "
                         "on declarative mixin classes.")
+                elif isinstance(obj, _memoized_declared_attr): # and \
+                    if obj._cascading:
+                        dict_[name] = ret = obj.__get__(obj, cls)
+                    else:
+                        dict_[name] = ret = getattr(cls, name)
+                    if isinstance(ret, (Column, MapperProperty)) and \
+                            ret.doc is None:
+                        ret.doc = obj.__doc__
                 elif isinstance(obj, declarative_props):
                     dict_[name] = ret = \
                         column_copies[obj] = getattr(cls, name)
@@ -148,6 +156,7 @@ def _as_declarative(cls, classname, dict_):
 
     clsregistry.add_class(classname, cls)
     our_stuff = util.OrderedDict()
+    add_later = util.OrderedDict()
 
     for k in list(dict_):
 
@@ -157,7 +166,10 @@ def _as_declarative(cls, classname, dict_):
 
         value = dict_[k]
         if isinstance(value, declarative_props):
-            value = getattr(cls, k)
+            if value.defer_defer_defer:
+                add_later[k] = value
+            else:
+                value = getattr(cls, k)
 
         elif isinstance(value, QueryableAttribute) and \
                 value.class_ is not cls and \
@@ -324,7 +336,8 @@ def _as_declarative(cls, classname, dict_):
                  declared_columns,
                  column_copies,
                  our_stuff,
-                 mapper_args_fn)
+                 mapper_args_fn,
+                 add_later)
     if not defer_map:
         mt.map()
 
@@ -339,7 +352,8 @@ class _MapperConfig(object):
                  inherits,
                  declared_columns,
                  column_copies,
-                 properties, mapper_args_fn):
+                 properties, mapper_args_fn,
+                 add_later):
         self.mapper_cls = mapper_cls
         self.cls = cls
         self.local_table = table
@@ -348,6 +362,7 @@ class _MapperConfig(object):
         self.mapper_args_fn = mapper_args_fn
         self.declared_columns = declared_columns
         self.column_copies = column_copies
+        self.add_later = add_later
 
     def _prepare_mapper_arguments(self):
         properties = self.properties
@@ -410,6 +425,8 @@ class _MapperConfig(object):
             self.local_table,
             **mapper_args
         )
+        for k, v in self.add_later.items():
+            setattr(self.cls, k, v.fget(self.cls))
 
 
 class _DeferredMapperConfig(_MapperConfig):
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index f3af46c..ea8c32f 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -1161,8 +1161,10 @@ class Column(SchemaItem, ColumnClause):
         existing = getattr(self, 'table', None)
         if existing is not None and existing is not table:
             raise exc.ArgumentError(
-                "Column object already assigned to Table '%s'" %
-                existing.description)
+                "Column object '%s' already assigned to Table '%s'" % (
+                    self.key,
+                    existing.description
+                ))
 
         if self.key in table._columns:
             col = table._columns.get(self.key)
</code></pre></div>