Repository: kedro-org/kedro
File: kedro/pipeline/node.py (Python)

Code before:
            Return the set of all assigned tags to the node.

        """
        return set(self._tags)

    def tag(self, tags: str | Iterable[str]) -> Node:
        """Create a new ``Node`` which is an exact copy of the current one,
            but with more tags added to it.

        Args:
            tags: The tags to be added to the new node.

        Returns:
            A copy of the current ``Node`` object with the tags added.

        """
        return self._copy(tags=self.tags | set(_to_list(tags)))

    @property
    def name(self) -> str:
        """Node's name.

        Returns:
            Node's name if provided or the name of its function.
        """
        node_name = self._name or self._func_name
        if self.namespace:
            return f"{self.namespace}.{node_name}"
        return node_name

    @property
    def short_name(self) -> str:
        """Node's name.

        Returns:
            Returns a short, user-friendly name that is not guaranteed to be unique.
            The namespace is stripped out of the node name.
        """
        if self._name:
            return self._name

        return self._func_name.replace("_", " ").title()

    @property
    def namespace(self) -> str | None:
        """Node's namespace.

        Returns:
            String representing node's namespace, typically from outer to inner scopes.
        """
        return self._namespace

Review comment (suggestion):
IIUC this is one of the key bits, right? `name` is a calculated property, and instead of using `__str__` below, it's using `._func_name` in the absence of a given `._name`

Code after:
    def func(self, func: Callable) -> None:
        """Sets the underlying function of the node.
        Useful if user wants to decorate the function in a node's Hook implementation.

        Args:
            func: The new function for node's execution.
        """
        self._func = func

        del self.inputs  # clear cached inputs

    @property
    def tags(self) -> set[str]:
        """Return the tags assigned to the node.

        Returns:
            Return the set of all assigned tags to the node.

        """
        return set(self._tags)

    def tag(self, tags: str | Iterable[str]) -> Node:
        """Create a new ``Node`` which is an exact copy of the current one,
            but with more tags added to it.

        Args:
            tags: The tags to be added to the new node.

        Returns:
            A copy of the current ``Node`` object with the tags added.

        """
        return self._copy(tags=self.tags | set(_to_list(tags)))

    @property
    def name(self) -> str:
        """Node's name.

        Returns:
            Node's name if provided or the name of its function.
        """
        node_name = self._name or self._set_unique_name()
        if self.namespace:
            return f"{self.namespace}.{node_name}"
        return node_name

    def _set_unique_name(self) -> str:
        """Set a unique name for the node."""
        if isinstance(self._func, partial):
            base = f"partial({self._func.func.__name__})"  # Use the original function's name
            key = f"{base}|{self.inputs}|{self.outputs}"