GildedRose refactoring kata
The individual methods in your code are fine; the structure is what hurts its maintainability. By adding specific update methods to the main Item class, you are giving it a huge amount of responsibility. Adding a layer of abstraction could let you write a simpler Item class and implement the specifics of the item aging process somewhere else.
The trick with this challenge is to recognize the assumptions that are being made by all items, and which parts of the program change for different items. For example, the update logic only ever affects the quality and sell_in properties. Furthermore, these properties change based only on the state of that individual item.
With this in mind, the item class can be written as this, where ager() returns an object with a nextQuality() and a nextSellIn() method, which return the item's values for the next day.
class Item:
    def __init__(self,name,sell_in,quality):
        self.ager = ager(name)

        self.name = name
        self.sell_in = sell_in
        self.quality = quality

    def age(self):
        self.quality = self.ager.nextQuality(self)
        self.sell_in = self.ager.nextSellIn(self)

    def __repr__(self):
        return &quot;%s, %s, %s&quot; % (self.name, self.sell_in, self.quality)

Then, an ager could be written like this, taking in the item as an input.
class DefaultAger:
    @staticmethod
    def nextQuality(item):
        return clamp_quality(item.quality-1)
    @staticmethod
    def nextSellIn(item):
        return item.sell_in-1

Right, we need that clamp_quality(). There's no need to mutate the item with this function, as the mutation already occurs in the age() method of Item. clamper() returns a function that clamps values between lo and hi.
def clamper(lo,hi):
    return lambda val:max(lo,min(hi,val))
clamp_quality = clamper(0,50)

With all of this, we can add a barebones implementaton of ager(). This will handle all items that don't have any special rules.
def ager():
  return DefaultAger

Okay that's great and all, but the entire point of this program is the edge cases. Lets get those in. All of this logic can be handled using more Ager objects.
class ConjuredAger(DefaultAger):
    @staticmethod
    def nextQuality(item):
        return clamp_quality(item.quality-2)

class LegendaryAger(DefaultAger):
    @staticmethod
    def nextQuality(item):
        return item.quality
    @staticmethod
    def nextSellIn(item):
        return item.sell_in

class BackstageAger(DefaultAger):
    @staticmethod
    def nextQuality(item):
        if item.sell_in&lt;=0:
            return 0
        if item.sell_in&lt;5:
            return clamp_quality(item.quality+3)
        if item.sell_in&lt;10:
            return clamp_quality(item.quality+2)
        return clamp_quality(item.quality+1)

Now for the beautiful part. If we dynamically give each item the correct ager, then all we need to do is call item.age() to update an item.
#maps items by name to the correct ager
agers={
    &quot;Aged Brie&quot;:BackstageAger,
    &quot;Sulfuras&quot;:LegendaryAger,
    &quot;Backstage Pass&quot;:BackstageAger,
    &quot;Mystical Hat&quot;:ConjuredAger
}
def ager(name):
    if name in agers:
        return agers[name]
    else:
        return DefaultAger

To verify it works:
items = [
    Item(&quot;Aged Brie&quot;,5,10),
    Item(&quot;Sulfuras&quot;,5,80),
    Item(&quot;Backstage Pass&quot;,5,20),
    Item(&quot;Jar&quot;,20,8)
]
# print this out to make sure we
# set all the agers correctly
print([
    item.ager for item in items
])

for i in range(10):
    print(&quot;========= Day %d =========&quot;%i)
    for item in items:
        print(item)
        item.age()

I realize this probably comes off as me showing off my own solution to the problem , but it's the best way I could demonstrate this idea of delegating responsibility into smaller objects. If a new type of item were to be added, one would only need to write an ager and assign it; the Item class could remain untouched.