I'd store everything about items as pure data, and then traverse the data to determine how the item behaves.
For example, suppose item properties vary per item type (instead of per item instance), and there can be multiple properties per item type. This could be set up as a set of lookup tables on the item type. Example in JSON format:
restrictions = {
'ghost_amulet': ['must_be_dead'],
'magic_sword': ['in_combat'],
…
};
actions = {
'ghost_amulet': [('revive', 0)], # note: int parameter not used for revive
'magic_sword': [('fire_damage', +3), ('ice_damage', -1)],
'wooden_shield': [('armor_modifier', +5), ('block_modifier', +3)],
…
}
Each action and restriction would map to a piece of code that implements it. Note that instead of restricting items to have only one type of modifier, I'd start out with having any item have any action or restriction. It's simpler and more flexible to put all of this into a data file than to use a class hierarchy that has to be recompiled when you change something. If there are space/speed issues you can add restrictions to streamline the data format.
There are lots of variants of this. There might be properties that vary per item instance. There might be more parameters for some actions. The action names might correspond to method names in the code instead of fields to modify. Actions and restrictions might be ints instead of strings. If only one restriction/action is needed you wouldn't need a list. Modifiers might be put into their own lookup table (for example you could make a fire_damage lookup table that maps to numbers). All of these details will vary by game.