pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.5k stars 525 forks source link

Passing arguments dict_keys(['add_self']) to Machine() caused an inheritance error #417

Closed xiaohuihui1024 closed 4 years ago

xiaohuihui1024 commented 4 years ago

Hi, @aleneum Using: transitions 0.8.0+

The alternative-initialization-patterns section of the documentation has the following code:

from transitions import Machine
class Matter():
    pass

lump1 = Matter()
lump2 = Matter()

machine = Machine(states=states, transitions=transitions, initial='solid', add_self=False)

machine.add_model(lump1)
machine.add_model(lump2, initial='liquid')

lump1.state  # 'solid'
lump2.state  # 'liquid'

machine.remove_model([lump1, lump2])
del lump1  # lump1 is garbage collected
del lump2  # lump2 is garbage collected

The add_self parameter in the machine initialization parameters may be invalid, it will cause an error

TypeError                                 Traceback (most recent call last)
~/anaconda3/lib/python3.7/site-packages/transitions/core.py in __init__(self, model, states, initial, transitions, send_event, auto_transitions, ordered_transitions, ignore_invalid_triggers, before_state_change, after_state_change, name, queued, prepare_event, finalize_event, model_attribute, **kwargs)
    533         try:
--> 534             super(Machine, self).__init__(**kwargs)
    535         except TypeError as err:

TypeError: object.__init__() takes exactly one argument (the instance to initialize)

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-3-d3b64a9ef538> in <module>
      6 lump2 = Matter()
      7 
----> 8 machine = Machine(states=states, transitions=transitions, initial='solid', add_self=False)
      9 
     10 machine.add_model(lump1)

~/anaconda3/lib/python3.7/site-packages/transitions/core.py in __init__(self, model, states, initial, transitions, send_event, auto_transitions, ordered_transitions, ignore_invalid_triggers, before_state_change, after_state_change, name, queued, prepare_event, finalize_event, model_attribute, **kwargs)
    534             super(Machine, self).__init__(**kwargs)
    535         except TypeError as err:
--> 536             raise ValueError('Passing arguments {0} caused an inheritance error: {1}'.format(kwargs.keys(), err))
    537 
    538         # initialize protected attributes first

ValueError: Passing arguments dict_keys(['add_self']) caused an inheritance error: object.__init__() takes exactly one argument (the instance to initialize)

I don’t quite understand how to use add_self correctly

In addition, this part of the document mentions:

If you don't provide an initial state in the state machine constructor, you must provide one every time you add a model:

machine = Machine(states=states, transitions=transitions, add_self=False)
machine.add_model(Matter())
>>> "MachineError: No initial state configured for machine, must specify when adding model."
machine.add_model(Matter(), initial='liquid')

I can understand the meaning of the sample code, but in fact it does not work like the above code.

# my sample code
from transitions import Machine
states=['solid', 'liquid', 'gas', 'plasma']
transitions = [
    { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },  # 熔化:固态->液态
    { 'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas' },  # 蒸发:液态->气态
    { 'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas' },  # 升华: 固态->气态
    { 'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma' }  # 电离:气态->等离子体
]
class Matter():
    pass
# Not working properly due to the above reasons
# machine = Machine(states=states, transitions=transitions, add_self=False)

# Remove the add_self parameter
machine = Machine(states=states, transitions=transitions)

machine.add_model(Matter())

machine has no an initial state (at least not specified in the initialization parameters). machine.add_model(Matter()) also did not specify an initial state for Matter() It didn't occur the error in the document example, but it works normally.

I try to verify what the initial state of the model is:

machine.models[0].state

The result is as follows

'initial'

I guess it should be added to the initial state by default

aleneum commented 4 years ago

Hi @xiaohuihui1024,

sorry, this part was (heavily) outdated. 'add_self' had been removed in August 2017 in 0.6.0. If you want to initialize a machine without a model, you can pass model=None or model=[]. The default value for model is 'self' which will add the machine itself.

In addition, this part of the document mentions: [...]

I corrected that paragraph since the default value for initial='initial'. It used to be initial=None but added a state called 'initial' in that case as well. So, if you actually dont want an initial state you have to pass initial=None explicitely.

from transitions import Machine

class Matter(object):
    pass

machine = Machine(model=None, initial=None)
machine.add_model(Matter())
# >>> ValueError: No initial state configured for machine, must specify when adding model.
xiaohuihui1024 commented 4 years ago

Thanks for your quick reply. @aleneum I have used "transition" since version 0.8.0 I don’t know some of the previous history. This issue seems to have been resolved, and users who read the document later will also avoid encountering this issue repeatedly.

aleneum commented 4 years ago

This has been solved I guess. I will close this for now. Feel free to comment if this still is an issue. I will reopen it if necessary.