hay varias formas de hacer  esto, pero me parece oportuno aclararte  algunos conceptos que si bien no están mal, es mejor hacerlos de otra forma.
- class Automovil(): 
-     def __init__(self, ruedas, puertas): 
-         self.ruedas = ruedas 
-         self.puertas = puertas 
-   
-     def mostrar(self): 
-         return(f"""ruedas...: {self.ruedas} 
- puertas..: {self.puertas}""") 
1) es una buena práctica usar return en las funciones sino después está la típica pregunta de ¿por qué sale un None ? las funciones deben, aunque no es obligatorio retornar algo
2)cambié la forma de mostrar la cadena, solo porque me parece mucho más claro la lectura
3)self no es una palabra reservada, pero por convención es la más usada, sobre todo porque en códigos extensos y también para otros programadores es más simple la comprensión del código, lo mismo para uno en el paso del tiempo.
4)Si bien este ejemplo es una forma de compartir métodos para mostrar los atributos se usa mucho más el método __str__() en lugar de difinir uno propio.
- class Audi(Automovil): 
-     def __init__(self, ruedas, puertas, color, modelo): 
-         Automovil.__init__(self,ruedas, puertas) 
-         self.color = color 
-         self.modelo = modelo 
-   
-     def mostrar(self): 
-         return(f"""{Automovil.mostrar(self)} 
- color....: {self.color} 
- modelo...: {self.modelo}""")  
como dije, se puede, ya que no es obligatorio hacer de otras formas, aunque en este caso no es necesario usar super, ya que directamente invoco al método __init__ de la clase padre.
- a = Automovil(4,5) 
- print(a.mostrar()) 
- b = Audi(4,4, 'azul',2020) 
- print(b.mostrar()) 
como me gusta escribir poco simplemente declaro los atributos.
Con respecto a lo de duplicados no lo son, ya que cada clase tiene sus propios atributos.