diff --git a/README.rst b/README.rst
index 1fe1c2f..fe1556a 100644
--- a/README.rst
+++ b/README.rst
@@ -20,10 +20,10 @@ Pyramid's traversal.
     class UserCollection(Branch):
         """User collection, for listings, or loading single users."""
 
-        def __load__(self, path):
+        def __load__(self, key):
             """Return child resource with requested user included."""
-            user = USERS[path]  # Load user or raise KeyError.
-            return self._sprout(key, user=user)
+            user = USERS[key]  # Load user or raise KeyError.
+            return self._child(key, user=user)
 
 
     @UserCollection.child_resource
diff --git a/kalpa/__init__.py b/kalpa/__init__.py
index ef7ddf2..61e6403 100644
--- a/kalpa/__init__.py
+++ b/kalpa/__init__.py
@@ -67,18 +67,29 @@ class Branch(with_metaclass(BranchType, Leaf)):
         """Dynamic resource loader for custom Branch classes.
 
         This method should either raise a KeyError or return an instantiated
-        resource class. Through one of _sprout(), _sprout_resource() or manual
-        instantiation. The response will be cached by the caller, __getitem__.
+        resource class. This can be achieved with the _child() method or manual
+        instantiation. The response will be cached by __getitem__().
         """
         raise KeyError(path)
 
-    def _sprout(self, _name, **attrs):
-        """Returns a child resource of the registered type."""
-        return self._CHILD_CLS(_name, self, **attrs)
+    def _child(self, *args, **attrs):
+        """Returns a newly instantiated child resource.
 
-    def _sprout_resource(self, _resource_cls, _name, **attrs):
-        """Returns a child resource of the provided type, with given name."""
-        return _resource_cls(_name, self, **attrs)
+        Positional arguments are either type+name or just a name. If the type
+        is not given, this defaults to the registered child resource class.
+
+        Additional named parameters are passed verbatim to the new instance.
+        """
+        if len(args) == 1:
+            if self._CHILD_CLS is None:
+                raise TypeError(
+                    'No child resource class is associated with %r. '
+                    'Provide one as the first argument' % self)
+            resource_class = self._CHILD_CLS
+            name, = args
+        else:
+            resource_class, name = args
+        return resource_class(name, self, **attrs)
 
     @classmethod
     def attach(cls, canonical_path, aliases=()):
@@ -92,7 +103,7 @@ class Branch(with_metaclass(BranchType, Leaf)):
 
     @classmethod
     def child_resource(cls, child_cls):
-        """Adds a resource class as the default child resource to sprout."""
+        """Adds a resource class as the default child class to instantiate."""
         cls._CHILD_CLS = child_cls
         return child_cls
 
diff --git a/setup.py b/setup.py
index a40c7f6..8691cf4 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@ def contents(filename):
 
 setup(
     name='kalpa',
-    version='0.3',
+    version='0.4',
     packages=find_packages(),
     author='Elmer de Looff',
     author_email='elmer.delooff@gmail.com',

Roll child-class spawning methods into one
The `_sprout` and `_sprout_resource` methods can be rolled into one with a variable number of positional arguments. The first one should be the key, the second (optional) argument is the resource class to use, which falls back to the child resource class.
diff --git a/kalpa/tests/test_kalpa.py b/kalpa/tests/test_kalpa.py
index 9f33402..615596c 100644
--- a/kalpa/tests/test_kalpa.py
+++ b/kalpa/tests/test_kalpa.py
@@ -44,12 +44,12 @@ def branching_tree():
     @Root.attach('objects')
     class Collection(Branch):
         def __load__(self, path):
-            return self._sprout(path, fruit='apple', twice=path * 2)
+            return self._child(path, fruit='apple', twice=path * 2)
 
     @Root.attach('people')
     class People(Branch):
         def __load__(self, path):
-            return self._sprout_resource(Person, path, first=path[0].upper())
+            return self._child(Person, path, first=path[0].upper())
 
     @Collection.child_resource
     class Object(Branch):
@@ -85,7 +85,7 @@ def mixed_tree():
 
     class Root(Root):
         def __load__(self, path):
-            return self._sprout(path)
+            return self._child(path)
 
     @Root.attach('spam')
     @Root.attach('eggs')
@@ -103,6 +103,43 @@ def mixed_tree():
         'static_cls': Static}
 
 
+@pytest.fixture
+def alternate_child_tree():
+    """Returns a resource tree where sub-resources can be of multiple types.
+
+    This implicitly tests the following parts of kalpa:
+
+        - Child resource assignment to Branch classes
+        - Child resource creation using the registered class
+    """
+    from kalpa import Root, Leaf
+
+    class Root(Root):
+        def __load__(self, name):
+            if name in ('adam', 'eve'):
+                return self._child(Admin, name)
+            return self._child(name)
+
+    class BadRoot(Root):
+        def __load__(self, name):
+            if name == 'explicit_none':
+                return self._child(None, name)
+            return self._child(name)
+
+    class Admin(Leaf):
+        pass
+
+    @Root.child_resource
+    class User(Leaf):
+        pass
+
+    return {
+        'root': Root(None),
+        'bad_root': BadRoot(None),
+        'admin_cls': Admin,
+        'user_cls': User}
+
+
 class TestBasicResource(object):
     def test_sub_resource_type(self, basic_tree):
         """Leaf is an instance of the Leaf class, for context selection."""
@@ -210,6 +247,33 @@ class TestMixedResource(object):
         assert isinstance(root['bacon'], mixed_tree['dynamic_cls'])
 
 
+class TestAlternateResourceClasses(object):
+    def test_common_child_resource(self, alternate_child_tree):
+        """"Retrieves an instance of the associated child class."""
+        root = alternate_child_tree['root']
+        assert type(root['john']) is alternate_child_tree['user_cls']
+
+    def test_alternate_child_resource(self, alternate_child_tree):
+        """Retrieves an instance of a loader-selected resource class."""
+        root = alternate_child_tree['root']
+        assert type(root['eve']) is alternate_child_tree['admin_cls']
+
+    def test_missing_child_resource(self, alternate_child_tree):
+        root = alternate_child_tree['bad_root']
+        with pytest.raises(TypeError) as excinfo:
+            root['adam']
+        # Check for the more informative message for the bad-config case
+        assert 'No child resource class is associated' in str(excinfo.value)
+        assert 'not callable' not in str(excinfo.value)
+
+    def test_bad_child_resource_class(self, alternate_child_tree):
+        root = alternate_child_tree['bad_root']
+        with pytest.raises(TypeError) as excinfo:
+            root['explicit_none']
+        # Check for the default Python non-callable error message
+        assert 'not callable' in str(excinfo.value)
+
+
 class TestAttributeAccess(object):
     def test_simple_attribute_access(self, branching_tree):
         """Keywords given for object creation are available as attributes."""

pip install -e .[test]
parse_log_pytest
pytest
pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
hupper==1.12.1
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
-e git+https://github.com/edelooff/kalpa.git@c351347bec34e43a01572d9cc5490bafc7be8ea9#egg=kalpa
packaging @ file:///croot/packaging_1734472117206/work
PasteDeploy==3.1.0
plaster==1.1.2
plaster-pastedeploy==1.0.1
pluggy @ file:///croot/pluggy_1733169602837/work
pyramid==2.0.2
pytest @ file:///croot/pytest_1738938843180/work
pytest-runner==6.0.1
six==1.17.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
translationstring==1.4
venusian==3.1.1
WebOb==1.8.9
zope.deprecation==5.1
zope.interface==7.2

name: kalpa
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - hupper==1.12.1
      - pastedeploy==3.1.0
      - plaster==1.1.2
      - plaster-pastedeploy==1.0.1
      - pyramid==2.0.2
      - pytest-runner==6.0.1
      - six==1.17.0
      - translationstring==1.4
      - venusian==3.1.1
      - webob==1.8.9
      - zope-deprecation==5.1
      - zope-interface==7.2
prefix: /opt/conda/envs/kalpa

kalpa/tests/test_kalpa.py::TestBranchingResource::test_loaded_object_type kalpa/tests/test_kalpa.py::TestBranchingResource::test_alternate_loaded_object_type kalpa/tests/test_kalpa.py::TestBranchingResource::test_object_lineage kalpa/tests/test_kalpa.py::TestBranchingResource::test_alternate_object_lineage kalpa/tests/test_kalpa.py::TestBranchingResource::test_object_caching kalpa/tests/test_kalpa.py::TestBranchingResource::test_alternate_object_caching kalpa/tests/test_kalpa.py::TestMixedResource::test_dynamic_resource_load kalpa/tests/test_kalpa.py::TestAlternateResourceClasses::test_common_child_resource kalpa/tests/test_kalpa.py::TestAlternateResourceClasses::test_alternate_child_resource kalpa/tests/test_kalpa.py::TestAlternateResourceClasses::test_missing_child_resource kalpa/tests/test_kalpa.py::TestAlternateResourceClasses::test_bad_child_resource_class kalpa/tests/test_kalpa.py::TestAttributeAccess::test_simple_attribute_access kalpa/tests/test_kalpa.py::TestAttributeAccess::test_delegated_attribute_access kalpa/tests/test_kalpa.py::TestAttributeAccess::test_attribute_cached_access kalpa/tests/test_kalpa.py::TestAttributeAccess::test_only_delegate_initial_attributes kalpa/tests/test_kalpa.py::TestAttributeAccess::test_only_delegate_initial_attribute_values kalpa/tests/test_kalpa.py::TestAttributeAccess::test_attributes_for_custom_children kalpa/tests/test_kalpa.py::TestPyramidTraversalIntegration::test_find_root kalpa/tests/test_kalpa.py::TestPyramidTraversalIntegration::test_find_resource kalpa/tests/test_kalpa.py::TestPyramidTraversalIntegration::test_resource_path kalpa/tests/test_kalpa.py::test_util_lineage kalpa/tests/test_kalpa.py::test_find_parent_by_class kalpa/tests/test_kalpa.py::test_find_parent_by_name
kalpa/tests/test_kalpa.py::TestBasicResource::test_sub_resource_type kalpa/tests/test_kalpa.py::TestBasicResource::test_sub_resource_lineage kalpa/tests/test_kalpa.py::TestBasicResource::test_keyerror_for_nonexistant_sub_resource kalpa/tests/test_kalpa.py::TestBasicResource::test_multiple_lookups_same_resource kalpa/tests/test_kalpa.py::TestBranchAliasing::test_alias_access kalpa/tests/test_kalpa.py::TestBranchAliasing::test_alias_resouce_caching kalpa/tests/test_kalpa.py::TestBranchAliasing::test_alias_are_separate_instances kalpa/tests/test_kalpa.py::TestMixedResource::test_static_resource_load kalpa/tests/test_kalpa.py::TestPyramidTraversalIntegration::test_resource_path_aliased kalpa/tests/test_kalpa.py::test_separate_subpath_registries kalpa/tests/test_kalpa.py::test_find_parent_by_class_no_match kalpa/tests/test_kalpa.py::test_find_parent_by_name_no_match
README.rst kalpa/__init__.py setup.py
README.rst kalpa/__init__.py setup.py