鱼C论坛

 找回密码
 立即注册
查看: 1270|回复: 2

[已解决]小问题,类实例里面传多个实参.

[复制链接]
发表于 2018-5-14 00:05:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 AKARocco 于 2018-5-14 00:14 编辑

#这个是基类.
class Restaurant():
    def __init__(self,restaurant_name,cuisine_type,):
        self.name = restaurant_name
        self.type = cuisine_type
        self.number_served = 0
        
    def describe_restaurant(self):
        print('The restaurant name is ' + self.name + " ")
        print('It has ' + self.type + ' ')
        print('现在一共有' + str(self.number_served) + '人')  
        
    def open_restaurant(self):
        print('The restaurant is open!')
        
    def set_number_served(self,people):
        self.number_served = people
        print('现在一共有' + str(self.number_served) + '人')  
    def  incre_number_served(self,people):
        self.number_served += people
        print('现在一共有' + str(self.number_served) + '人')  
my_restaurant = Restaurant('金拱门','快餐')
my_restaurant.open_restaurant()
my_restaurant.describe_restaurant()
my_restaurant.set_number_served(100)
my_restaurant.incre_number_served(20)

#这个是子类
class IceCreamStand(Restaurant):
    def __init__ (self,restaurant_name,cuisine_type,*flavors):
        super().__init__(restaurant_name,cuisine_type)
        self.flavors = flavors
        
    def show_icecream(self):
        print(self.flavors)
            
eat_ic = IceCreamStand('香草','草莓','蓝莓')
eat_ic.show_icecream()





主要问题是继承和传递多个实参
想给*flavors传多个实参,但是下面的香草,蓝莓被上面的restaurant_name和cuisine_type顶掉了.

最后打出来只能显示蓝莓.
怎么搞啊





最佳答案
2018-5-14 10:23:23
本帖最后由 ABC23 于 2018-5-14 10:32 编辑

class IceCreamStand(Restaurant):
    def __init__ (self,restaurant_name,cuisine_type,*flavors):
        super().__init__(restaurant_name,cuisine_type)
        self.flavors = flavors

======================
既然在init函数中写了参数,就要用。这是基本的考虑——不然你为什么要传递呢?对吗。
如果不想传递父类的init相关的参数,可以在这之前先把相关参数固定好。
比如可以为父类参数指定默认值。

class Restaurant:
        def __init__(self, restaurant_name,cuisine_type)
        ...

你担心的不就是父类的restaurant_name和cuisine_type参数嘛?首先,既然 IceCreamStand类继承了饭店类,就是它的子类。
其次,【子类不一定非要调用父类的init方法】。
请看下面的例子;

>>> class A:
        def __init__(self, a):
                self.a = a

>>> class B(A):
        def __init__(self, b):
                self.b = b

>>> class C(A):
        def __init__(self, c):
                super().__init__(1)
                self.c = c

B、C都继承自A,其中C比较传统,调用了A的init方法进行初始化,而B却没有。

>>> b = B(1)
>>> b.__dict__
{'b': 1}

>>> c = C(1)
>>> c.__dict__
{'a': 1, 'c': 1}

看到了么?C类的实例对象拥有来自A类的【实例属性】a,但是B类的实例对象却没有。因为B类没有调用A类的init方法。

这就是代码语言的灵活。一点和包括Java等一些语言都不同,它们在构造方法中要传入父类的构造方法,还要写在子类construcor的第一行。
(这样说不严谨,实际上是调用了父类的__new__方法,隐式的传递了构造实例的基本“配置信息”,但是却没有传递别的【用户自定义参数】(如这里的xxname),Java做了简化在constructor中实际上做了new和init的工作)。

所以呢,回归主题。
你这里【既可以传递父类的属性,调用父类的init方法,也可以不这样做】。

>>> class A:
        def __init__(self, a):
                self.a = a
        def fa(self):
                print('function of a...')

               
>>> class B(A):
        def __init__(self, b):
                self.b = b


               
>>> class C(A):
        def __init__(self, c, ap=1):
                super().__init__(ap)
                self.c = c

               
>>> b = B(1)
>>> c = C(1)
>>> dir(b)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'b', 'fa']
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'c', 'fa']
>>> b.fa()
function of a...
>>> c.fa()
function of a...

上面b对象没有调用父类的init方法,于是没有得到A的实例属性:a。对象c做了于是得到了。
不论是b、c,都得到了来自A()的fa()方法。
解释:Python的函数,包括在类中加载的方法是【一等公民】,和对应的模块、类处于同等地位(在加载的时候)。
【但是属性不同,我是说实例属性】。

==============================

最后总结,如果你不想在子类的init方法中传递父类参数,可以对指定子类传递默认父类参数(还是调用父类的init方法吧,免得到时候要用A()的实例属性):

方法一
def __init__(self, 子类参数):
        super().__init(父类参数)
        ...

方法二
def __init__(self, 子类参数, 父类参数=默认值):
        super().__init(父类参数)
        ...

还是建议使用方法二,灵活性更大。



>>> dis(B)
Disassembly of __init__:
  3           0 LOAD_FAST                1 (b)
              2 LOAD_FAST                0 (self)
              4 STORE_ATTR               0 (b)
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE

>>> dis(C)
Disassembly of __init__:
  3           0 LOAD_GLOBAL              0 (super)
              2 CALL_FUNCTION            0
              4 LOAD_ATTR                1 (__init__)
              6 LOAD_FAST                2 (ap)
              8 CALL_FUNCTION            1
             10 POP_TOP

  4          12 LOAD_FAST                1 (c)
             14 LOAD_FAST                0 (self)
             16 STORE_ATTR               2 (c)
             18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

这是B、C的字节码。其中B压根就没有执行父类的初始化函数。

【Python的初始化函数不等于构造方法,在实例对象<font color=red>创建完成后</font>调用】。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-5-14 08:59:28 | 显示全部楼层
在此之前将父类需要初始化的参数传递进去
比如
eat_ic = IceCreamStand('星巴克','甜品','香草','草莓','蓝莓')
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-14 10:23:23 | 显示全部楼层    本楼为最佳答案   
本帖最后由 ABC23 于 2018-5-14 10:32 编辑

class IceCreamStand(Restaurant):
    def __init__ (self,restaurant_name,cuisine_type,*flavors):
        super().__init__(restaurant_name,cuisine_type)
        self.flavors = flavors

======================
既然在init函数中写了参数,就要用。这是基本的考虑——不然你为什么要传递呢?对吗。
如果不想传递父类的init相关的参数,可以在这之前先把相关参数固定好。
比如可以为父类参数指定默认值。

class Restaurant:
        def __init__(self, restaurant_name,cuisine_type)
        ...

你担心的不就是父类的restaurant_name和cuisine_type参数嘛?首先,既然 IceCreamStand类继承了饭店类,就是它的子类。
其次,【子类不一定非要调用父类的init方法】。
请看下面的例子;

>>> class A:
        def __init__(self, a):
                self.a = a

>>> class B(A):
        def __init__(self, b):
                self.b = b

>>> class C(A):
        def __init__(self, c):
                super().__init__(1)
                self.c = c

B、C都继承自A,其中C比较传统,调用了A的init方法进行初始化,而B却没有。

>>> b = B(1)
>>> b.__dict__
{'b': 1}

>>> c = C(1)
>>> c.__dict__
{'a': 1, 'c': 1}

看到了么?C类的实例对象拥有来自A类的【实例属性】a,但是B类的实例对象却没有。因为B类没有调用A类的init方法。

这就是代码语言的灵活。一点和包括Java等一些语言都不同,它们在构造方法中要传入父类的构造方法,还要写在子类construcor的第一行。
(这样说不严谨,实际上是调用了父类的__new__方法,隐式的传递了构造实例的基本“配置信息”,但是却没有传递别的【用户自定义参数】(如这里的xxname),Java做了简化在constructor中实际上做了new和init的工作)。

所以呢,回归主题。
你这里【既可以传递父类的属性,调用父类的init方法,也可以不这样做】。

>>> class A:
        def __init__(self, a):
                self.a = a
        def fa(self):
                print('function of a...')

               
>>> class B(A):
        def __init__(self, b):
                self.b = b


               
>>> class C(A):
        def __init__(self, c, ap=1):
                super().__init__(ap)
                self.c = c

               
>>> b = B(1)
>>> c = C(1)
>>> dir(b)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'b', 'fa']
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'c', 'fa']
>>> b.fa()
function of a...
>>> c.fa()
function of a...

上面b对象没有调用父类的init方法,于是没有得到A的实例属性:a。对象c做了于是得到了。
不论是b、c,都得到了来自A()的fa()方法。
解释:Python的函数,包括在类中加载的方法是【一等公民】,和对应的模块、类处于同等地位(在加载的时候)。
【但是属性不同,我是说实例属性】。

==============================

最后总结,如果你不想在子类的init方法中传递父类参数,可以对指定子类传递默认父类参数(还是调用父类的init方法吧,免得到时候要用A()的实例属性):

方法一
def __init__(self, 子类参数):
        super().__init(父类参数)
        ...

方法二
def __init__(self, 子类参数, 父类参数=默认值):
        super().__init(父类参数)
        ...

还是建议使用方法二,灵活性更大。



>>> dis(B)
Disassembly of __init__:
  3           0 LOAD_FAST                1 (b)
              2 LOAD_FAST                0 (self)
              4 STORE_ATTR               0 (b)
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE

>>> dis(C)
Disassembly of __init__:
  3           0 LOAD_GLOBAL              0 (super)
              2 CALL_FUNCTION            0
              4 LOAD_ATTR                1 (__init__)
              6 LOAD_FAST                2 (ap)
              8 CALL_FUNCTION            1
             10 POP_TOP

  4          12 LOAD_FAST                1 (c)
             14 LOAD_FAST                0 (self)
             16 STORE_ATTR               2 (c)
             18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

这是B、C的字节码。其中B压根就没有执行父类的初始化函数。

【Python的初始化函数不等于构造方法,在实例对象<font color=red>创建完成后</font>调用】。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-3-11 14:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表