use v5.12; use strict; use warnings; use MRO::Compat; package My::Base { my %_attr_data; my %_mro_cache; sub _get_attr_data { my $package = shift; my @parents = @{ $_mro_cache{$package} ||= mro::get_linear_isa($package) }; my %return; for my $parent (@parents) { ref $_attr_data{$parent} or next; for my $attr (keys %{$_attr_data{$parent}}) { $return{$attr} ||= $_attr_data{$parent}{$attr}; } } return %return; } sub _set_addr_data { my $package = shift; $package eq caller or die; $_attr_data{$package} = ref($_[0]) ? $_[0] : {@_}; } __PACKAGE__->_set_addr_data( '_root_entry1' => [ undef, 'read/write'], '_root_entry2' => [ undef, 'read'], ); } package My::Derived { use parent -norequire, 'My::Base'; __PACKAGE__->_set_addr_data( '__some_entry1' => [ undef, 'read' ], ); } package My::Derived::Derived { use parent -norequire, 'My::Derived'; __PACKAGE__->_set_addr_data( '__some_entry1' => [ 123, 'read/write' ], '__some_entry2' => [ 456, 'read/write' ], ); } use Data::Dumper; print Dumper { My::Derived::Derived->_get_attr_data };