1.一个Python中“多态”案例中的“关于参数(局部变量)调用其他类的属性和方法的疑难分析
(总共有3部分)
(一)
上面代码中dog参数只要在方法中定义了就能直接使用并且能调用name属性和game方法是吗?
———————————
是的,你的理解是正确的!在 Python 中,只要 `dog` 参数被传入方法(`game_with_dog`),就可以直接使用它,并调用它的属性和方法(如 `dog.name` 和 `dog.game()`),前提是:
1. `dog` 对象确实有这些属性和方法(即它所属的类定义了 `name` 属性和 `game()` 方法)。
2. `dog` 是方法的参数(或局部变量),而不是类的属性,所以不需要 `self.dog`。
---
详细解释:
1.`dog` 是方法的参数,可以直接使用
在 `Person` 类的 `game_with_dog` 方法中:
代码示例→
def game_with_dog(self, dog): # dog 是参数
print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name)) # 直接使用 dog.name
dog.game() # 直接调用 dog.game()
```
- `dog` 是方法的参数,Python ⭐️允许直接使用它,无需额外声明。
- 只要⭐️调用方法时传入的对象(如 `wangcai`)具有 `name` 属性和 `game()` 方法,代码就能正常运行。
是的,在代码 “def game_with_dog(self, dog):” 中⭐️⭐️dog 是方法的参数,它接收的是一个类的实例(对象),⭐️⭐️或者说dog是一个对象。
---
2. 为什么 `dog` 能调用 `name` 和 `game()`?
- `dog.name`:
- `Dog` 类和 `XiaoTianDog` 类都在 `__init__` 中定义了 `self.name = name`,所以它们的实例都有 `name` 属性。
- 因此,`dog.name` 可以正确访问。
- `dog.game()`:
- `Dog` 类定义了 `game()` 方法,`XiaoTianDog` 继承并重写了它。
- 由于 `dog` 可能是 `Dog` 或 `XiaoTianDog` 的实例,调用 `dog.game()` 时会自动执行对应类的方法(多态)。
⭐️dog确实是Dog/XiaoTianDog的示例:
因为→→→
⭐️⭐️⭐️
①xiaoming.play_with_dog(wangcai),即dog传入对象是wangcai→②而wangcai = XiaoTianDog("飞天旺财"),即wangcai是XiaoTianDog的示例对象→③所以最终dog就是XiaoTianDog的示例对象,能调用XiaoTianDog的game方法、即:dog.game();而XiaoTianDog继承了Dog所以拥有Dog的name属性,能调用Dog的name属性、即:dog.name
---
3. 为什么不需要 `self.dog`?
- `self.dog` 表示访问当前对象(`Person` 实例)的 `dog` 属性,但 `Person` 类并没有定义这个属性。
- 这里的 `dog` 是方法的局部变量(参数),不是类的属性,所以直接使用即可。
---
补:区分Python中的“函数和方法”、“属性和变量”
①函数和方法都是函数,定义在类内叫做方法,定义在类外或者单独使用叫做函数;
②属性和变量其实都是变量,定义在类内叫做属性,定义在类外或者单独使用叫做变量;
———
对比:如果 `dog` 是类的属性
假设 `Person` 类有一个 `dog` 属性:
代码示例→
class Person(object):
def __init__(self, name, dog): # 初始化时传入 dog
self.name = name
self.dog = dog # 将 dog 存储为实例属性
def play_with_dog(self):
print("%s 和 %s 玩耍..." % (self.name, self.dog.name)) # 必须用 self.dog
self.dog.game()
```
这时:
- `dog` 是 `Person` 的属性,必须通过 `self.dog` 访问。
- 调用方式会变成:
```python
wangcai = XiaoTianDog("飞天旺财")
xiaoming = Person("小明", wangcai) # 初始化时传入 dog
xiaoming.play_with_dog() # 方法内部用 self.dog
```
---
总结:
- 方法的参数(如 `dog`)可以直接使用,无需 `self.`。
- 只要传入的对象有对应的属性和方法(如 `name` 和 `game()`),就可以调用。
- 如果 `dog` 是类的属性(如 `self.dog`),则需要通过 `self.dog` 访问。
你的理解完全正确!这是 Python 面向对象编程中方法参数和对象动态调用的典型用法。
———————————
(二)
这些代码里的dog.name、dog.game分别是什么意思?dog是哪来的是什么角色?,为什么dog不用self.dog就能直接调用name和game方法?
在给定的代码中,`dog.name` 和 `dog.game()` 的用法涉及到面向对象编程中的对象和方法调用。让我逐步解释:
1. `dog` 的来源和角色
`dog` 是 `Person` 类中 `game_with_dog` 方法的一个参数。当调用 `xiaoming.game_with_dog(wangcai)` 时,`wangcai`(一个 `XiaoTianDog` 实例)被传递给 `game_with_dog` 方法,此时方法内的 `dog` 参数就指向 `wangcai` 对象。
2. `dog.name` 和 `dog.game()` 的含义
- `dog.name`:访问 `dog` 对象的 `name` 属性。因为 `Dog` 类(以及继承它的 `XiaoTianDog` 类)在 `__init__` 方法中定义了 `self.name = name`,所以 `dog` 对象有 `name` 属性。
- `dog.game()`:调用 `dog` 对象的 `game` 方法。如果 `dog` 是 `XiaoTianDog` 实例(如代码中的 `wangcai`),则会执行 `XiaoTianDog` 类中重写的 `game` 方法(输出“飞到天上去玩耍”);如果是普通的 `Dog` 实例,则执行 `Dog` 类的 `game` 方法(输出“蹦蹦跳跳的玩耍”)。
3. 为什么不需要 `self.dog`?
- `dog` 是方法的参数,不是类的属性。`self.dog` 会尝试访问当前对象(`Person` 实例)的 `dog` 属性,但这里并没有定义这个属性。
- 直接使用 `dog` 是因为它是一个局部变量(参数),指向传入的 `Dog` 或 `XiaoTianDog` 实例。Python 中方法的参数可以直接在方法内使用,无需通过 `self`。
代码执行流程
1. `wangcai = XiaoTianDog("飞天旺财")`:创建一个 `XiaoTianDog` 实例,名字为“飞天旺财”。
2. `xiaoming = Person("小明")`:创建一个 `Person` 实例,名字为“小明”。
3. `xiaoming.game_with_dog(wangcai)`:调用 `Person` 的 `game_with_dog` 方法,传入 `wangcai` 对象。
- 方法内打印:`小明 和 飞天旺财 快乐的玩耍...`(通过 `dog.name` 获取名字)。
- 调用 `dog.game()`,即 `wangcai.game()`,执行 `XiaoTianDog` 的 `game` 方法,输出:`飞天旺财 飞到天上去玩耍...`。
总结
- `dog` 是方法的参数,指向传入的 `Dog` 或子类实例。
- `dog.name` 和 `dog.game()` 是动态调用的,具体行为取决于 `dog` 的实际类型(多态)。
- 不需要 `self.dog` 因为 `dog` 不是类的属性,而是方法的局部变量。
———————————
(三)
class Cat:
def __init__(self,name2):
self.name1 = name2
#区分开二者:①name1是该Cat的属性(成员变量);②而name2是参数(传入参数)、用于接收外界传入的内容
def eat(self):
print("%s 爱吃鱼" % self.name1)
tom = Cat("Tom")
#Tom作为内容传给传入参数name2,然后name2将值(Tom)提供给内部的成员变量self.name1
——————————
在Python类中,self.name1 带上 self. 是为了明确这个变量是实例属性(instance attribute),而不是局部变量。这是面向对象编程中非常重要的概念,具体原因如下:
区分"实例属性"和"局部变量"→
①name2 是方法的参数,它是一个局部变量,只在 __init__ 方法的作用域内有效。②self.name1 是实例属性,它属于当前创建的实例对象(比如某只具体的猫),会随着对象的存在而一直存在。
通过 self. 显式绑定,可以确保这个属性归属于实例。
———————
①⭐️在类的外部,通过“变量名.”访问对象的属性和方法("变量名"可指局部变量,只要这个局部变量接收传入的对象是“具有name属性和game方法的类的实例对象”那么就能直接用“变量名.属性/方法”访问“属性/方法”了)
②在类封装的方法中,通过“self.”访问对象的属性和方法
————————————————————
2.“方法(类中的函数)”和“函数”的参数中要不要带self参数→
别搞混了,区分开!:
(1)类的方法(即类中的函数,需要 self):
class Dog:
def speak(self): # self 代表调用该方法的实例(如 dog)
print("Woof!")
dog = Dog()
dog.speak() # Python 自动把 dog 作为 self 传入
(2)普通函数(独立的函数,不需要 self):
def animal_sound(animal): # animal 是一个普通参数
animal.speak() # 调用 animal 的 speak 方法
animal_sound(dog) # 手动传入 dog 作为 animal
————————————————————
3.理清一个关于用“self.”访问属性的疑难
→关键是要理解self是什么角色
(1)问题:
class Person:
def __init__(self,name,weight):
self.name=name
self.weight=weight
def run(self):
print("%s 跑了一次步" % self.name)
self.weight -= 0.5
在类封装的方法中,通过“self.”访问对象的属性和方法。这句话中的方法是类中的各方法都可以用“self.”的方式访问对象的属性吗?
(2)解答:
是的,在 Python 的类中,所有实例方法(即第一个参数是 `self` 的方法)都可以通过 `self.` 访问当前对象的属性和其他方法。
关键点:
①.⭐️ `self` 代表当前对象实例
- 当调用 `person.run()` 时,`self` 会自动指向 `person` 这个实例对象。
- 因此,`self.name` 和 `self.weight` 访问的是当前对象的属性。
②. 所有实例方法共享 `self`
- 类中的任何实例方法(如 `run`、`eat`、`sleep` 等)都可以通过 `self.` 访问或修改对象的属性。
- 例如,你可以在另一个方法中直接读取 `self.weight` 或调用 `self.run()`。
③. 修改属性会影响对象的状态
- 比如 `run()` 方法中通过 `self.weight -= 0.5` 修改了对象的属性,其他方法调用时也会看到这个变化。
示例扩展:
class Person:
def __init__(self, name, weight):
self.name = name
self.weight = weight
def run(self):
print("%s 跑了一次步" % self.name)
self.weight -= 0.5
def eat(self, food_calories):
print("%s 吃了食物,增加了 %d 大卡" % (self.name, food_calories))
self.weight += food_calories / 1000 # 假设每1000大卡增加1kg
def check_weight(self):
print("%s 的当前体重是 %.1f kg" % (self.name, self.weight))
# 使用示例
person = Person("小明", 70)
person.run() # 体重减少0.5kg
person.eat(500) # 体重增加0.5kg
person.check_weight() # 输出当前体重(仍然是70kg)
```
总结:
- `self.` 是访问对象属性和方法的统一方式,所有实例方法均可使用。
- 通过 `self.` 修改的属性会持久化到对象中,其他方法调用时会反映这些变化。
————————————————————