How to best handle attribute initialisation? - Godot 3.5.3

Godot Version

3.5.3

Question

I have a simple example scene:

Node2D
-Label
-Label
-Label

I have a script attached to the Node2D:

extends Node2D


export (int) var unchanged_attribute = 1 setget set_unchanged_attribute
export (int) var changed_in_scene_attribute = 2 setget set_changed_in_scene_attribute
export (int) var changed_in_code_attribute = 3 setget set_changed_in_code_attribute


func set_unchanged_attribute(value):
	unchanged_attribute = value
	print("setting unchanged_attribute: ", value)


func set_changed_in_scene_attribute(value):
	changed_in_scene_attribute = value
	print("setting changed_in_scene_attribute: ", value)


func set_changed_in_code_attribute(value):
	changed_in_code_attribute = value
	print("setting changed_in_code_attribute: ", value)


func _ready():
	self.changed_in_code_attribute = 5
	$override_in_code_label.add_font_override("font", DynamicFont.new())
	$override_in_code_label.add_color_override("font_color", Color(1,0,0))
	$override_in_code_label.add_constant_override("shadow_offset_x", 10.0)

I also have a script attached to each of the Labels:

extends Label


func add_font_override(font_name, font):
	.add_font_override(font_name, font)
	print("overriding font on node: ", name)


func add_constant_override(c_name, value):
	.add_constant_override(c_name, value)
	print("overriding constant on node: ", name)


func add_color_override(c_name, value):
	.add_color_override(c_name, value)
	print("overriding color on node: ", name)

When I run the scene, I get following output:

setting changed_in_scene_attribute: 5
setting changed_in_code_attribute: 5
overriding font on node: override_in_code_label
overriding color on node: override_in_code_label
overriding constant on node: override_in_code_label

A little bit of explanation, though it should be easy to deduce what is going on here:
-The Node2D has three attributes:
unchanged_attribute - this one is left with default value in the scene.
changed_in_scene_attribute - this one has its value changed in the scene file.
changed_in_code_attribute - this one has its value set in _ready().
This demonstrates that the setter function does not fire for attributes with default values. I expect the setter to be fired in order to run some code which adjusts the state, like you would expect a Viewport to change after you’ve set its size property. I can manually invoke the setter in _ready(), however this is much more cumbersome as it needs to be done for every attribute.

In a much similar manner, there are three labels:
no_override_label - has no properties overridden.
override_in_scene_label - has a font, color and constant overridden in the scene file.
override_in_code_label - has a font, color and constant overridden in _ready() of the scene root.
Here, neither no overriding nor overriding in scene file fire the override methods. There are no signals that I know of that would fire.

My problem is I would like to override some default logic or at least add my own to setting attributes. As I said, I could invoke all the setters and handlers manually at init, but that would make them occasionally fire twice, is cumbersome and does not seem to be the “right” way of doing it. I have no idea how this sort of initialisation is meant to be handled in Godot.

The initial problem where I had the most issues was a Label which automatically change its font size to fit the text inside. Irrelevant here, but demonstrates how this is a meaningful problem.

I don’t have an answer, I just wanted to say I don’t really like get/set functions for the exact reason you pointed out.

But I don’t think there is any other way then just calling them in the ready function.

You could potentially programmatic it by adding a prefix/suffix the function/variable name then do a self.get_method_list and/orself.get_property_list, then call the function, or set the variable to itself.