Writing an equivalent Julia mutable struct that have similar functionality of a Python class

I have the following Python class with a class counter and self-referential assignment as follows:

class Foo(object):

    counter = 0 #  a class attributeinitialized to 0 that is shared across all instances of the class Foo

    def __init__(self, bar=True, dict=None):
				 
        self._bar = bar # instance attribute `_bar` gets `bar`
		
        self._baz = None # instance attribute `_baz` gets `None`

        if bar:
            self.dict = {self: 1} # self-referential assignment
            self.counter = Foo.counter # instance attribute `counter` gets current value of the class attribute `counter`
            Foo.counter += 1 # increase  class attribute `counter` by 1, when bar is `True`
        else:
            self.dict = dict # instance attribute `dict` gets `dict`
            self.counter = None # instance attribute `counter` gets `None` when bar is `False`		

Sample instances would be

foo1 = Foo() 
print(f'foo1._bar: {foo1._bar}, foo1._baz: {foo1._baz}, foo1.dict: {foo1.dict}, foo1.counter: {foo1.counter}') 
# output
# foo1._bar: True, foo1._baz: None, foo1.dict: {<__main__.Foo object at 0x000002A50597E210>: 1}, foo1.counter: 0

custom_dict = {'a': 1, 'b': 2}
foo2 = Foo(bar=False, dict=custom_dict)
print(f'foo2._bar: {foo2._bar}, foo2._baz: {foo2._baz}, foo2.dict: {foo2.dict}, foo2.counter: {foo2.counter}') 
# output
# foo2._bar: False, foo2._baz: None, foo2.dict: {'a': 1, 'b': 2}, foo2.counter: None

foo3 = Foo() 
print(f'foo3._bar: {foo3._bar}, foo3._baz: {foo3._baz}, foo3.dict: {foo3.dict}, foo3.counter: {foo3.counter}') 
# output
# foo3._bar: True, foo3._baz: None, foo3.dict: {<__main__.Foo object at 0x000002A50597F7D0>: 1}, foo3.counter: 1

In Julia, I am trying to create a mutable struct called Foo that will be equivalent to the Python class Foo. In particular for my purpose it is better if I can instantiate similarly to the Python code, e.g., foo 1 = Foo(), foo2 = Foo(bar=False, dict=custom_dict) and so on. I will appreciate any tips or suggestions regarding how to do that.

This design doesn’t translate very well to Julia (particularly the global mutable counter), so it may be a case of the XY Problem. What is your underlying problem?

2 Likes

Thanks for your response. I am not looking for the exact line-by-line conversion into Julia; equivalent Julia code with the same functionality will suffice for my purpose. That being said, I need a global variable in Julia that keeps track of the number of instances with _bar being true.

This seems very similar to your prior question:

It would be great if you could attempt the Julia solution given the prior knowledge and show us your attempt in the future.

julia> let counter = 0
           global Foo
           Base.@kwdef mutable struct Foo
               bar::Bool
               baz::Any
               counter::Union{Int, Nothing}
               dict::Dict
           end
               function Foo(;bar=true, dict=Dict{Foo,Int}())
                   self = Foo(bar, nothing, nothing, dict)
                   if bar
                       self.dict = Dict(self => 1)
                       self.counter = counter
                       counter += 1
                   end
                   return self
               end
       end
Foo

julia> Base.show(io::IO, f::Foo) =
           print(io, """
           Foo(
               bar=$(f.bar),
               baz=$(f.baz),
               dict=$(f.bar ?
                   "Dict(#=circular reference=# => 1)" :
                   f.dict
               ),
               counter=$(f.counter)
           )""")

julia> foo1 = Foo()
Foo(
    bar=true,
    baz=nothing,
    dict=Dict(#=circular reference=# => 1),
    counter=0
)

julia> custom_dict = Dict('a' => 1, 'b' => 2)
Dict{Char, Int64} with 2 entries:
  'a' => 1
  'b' => 2

julia> foo2 = Foo(bar=false, dict=custom_dict)
Foo(
    bar=false,
    baz=nothing,
    dict=Dict('a' => 1, 'b' => 2),
    counter=nothing
)

julia> foo3 = Foo(); @show foo3;
foo3 = Foo(
    bar=true,
    baz=nothing,
    dict=Dict(#=circular reference=# => 1),
    counter=1
)

Yes, the questions are very similar, except in this case, a self-reference is needed, and I was not sure how to do that in Julia. Thanks @mkitti.