概念
Python 一切皆对象 object
,最典型的对象类 class
与类的实例 instance
一、对象的属性
-
类中可定义属性。为类创建实例对象时,首先,运行类的
__new__
方法将类实例化,接着,运行类的__init__
初始化方法,其中可定义实例的属性 -
实例自动继承类的属性,同时也会通过
__init__
初始化方法获得实例属性,发生命名空间冲突即出现属性名重合时,优先级更高的实例属性会重写override
(如果是函数需要使参数列表与原函数一致,返回类型与原函数一致)继承的类属性
一、对象的方法
Python 中方法 method
的内涵基于面向对象的编程语境,即被对象绑定的函数就是方法,否则就是函数 function
-
「类方法」需要通过装饰器
@classmethod
包装,需要定义参数cls
,参数传入时cls
只能是类对象。类方法可以被类与实例对象调用,类对象调用时,Python 会将调用类方法的类对象传入cls
,实例对象调用时,Python 也会将调用类方法的实例对象所继承的类对象传入cls
。在类方法内通过cls.method()
理论上可以调用除实例方法外的任何方法 -
「实例方法」需要定义参数
self
,参数传入时self
只能是实例对象,Python 在调用时会将调用实例方法的对象传入self
,因此只能通过实例对象调用实例方法。在实例方法内通过self.method()
理论上可以调用类中的任何方法 -
「普通方法」类对象可以直接调用,实例对象只能调用定义
*args
可变参数的普通方法。在普通方法内理论上可以通过classname.method()
调用除实例方法外的任何方法 -
「静态方法」通过装饰器
@staticmethod
实现,类与实例对象都能直接调用。在方法内可以通过classname.method()
调用除实例方法外的任何方法 -
支持运算符重载
overload
(函数名相同但参数不同),可以对类的专有方法__str__
与__add__
等进行重载从而让类对象及其实例对象能够使用运算符操作
实践
class Toy(object):
count = 0
def who_toy():
print(Toy)
@classmethod
def show_toy_count(cls):
print('玩具对象的数量 %d' % cls.count, cls)
@staticmethod
def hi():
print('Hello!')
def __init__(self, name):
self.name = name
Toy.count += 1
def beybey(self):
self.hi()
print('Sad!', self)
# 类对象
print(Toy.count) # 输出:0
Toy.who_toy() # <class '__main__.Toy'>
Toy.show_toy_count() # 输出:玩具对象的数量 0 <class '__main__.Toy'>
Toy.hi() # 输出:Hello!
Toy.beybey() # 错误语法,self必须指向实例对象,此处实例方法指向类对象而不是实例对象
# 实例对象
toy1 = Toy('泰迪熊')
toy1.hand = 2
print(toy1.name) # 输出:泰迪熊
print(toy1.hand) # 输出:2
# 实例对象调用类方法时,与类对象调用类方法无异,但实际上调用仍通过实例对象继承的类对象实现
toy1.show_toy_count() # 输出:玩具对象的数量 1 <class '__main__.Toy'>
# 实例对象调用静态方法时,与类对象调用静态方法无异,但实际上调用仍通过实例对象继承的类对象
toy1.hi() # 输出:Hello!
# 实例对象调用实例方法,Python的解释器内部,当我们调用toy1.beybey()时,实际上Python解释成Toy.beybey(toy1)
toy1.beybey() # 输出:Hello! -> Sad! <__main__.Toy object at 0x104be6a30>
Toy.beybey(toy1) # 输出:Hello! -> Sad! <__main__.Toy object at 0x104be6a30>
# 隐式实例化并调用实例方法
Toy('哥斯拉').beybey() # 输出:Hello! -> Sad! <__main__.Toy object at 0x104be69d0>
print(Toy.hand) # AttributeError: type object 'Toy' has no attribute 'hand'
# 类与其实例的类型和内存地址
print(type(Toy), id(Toy), type(toy1), id(toy1)) # 输出:<class 'type'> 5274699520 <class '__main__.Toy'> 4374555184
print(int('0x104be6a30', 16)) # 输出:4374555184
class Cat: # 或者class Cat()不写父对象形式定义类对象,会默认继承祖先object对象
name = '小敏'
# 实例无法使用因为无法匹配参数
def smile():
print('哈哈~')
def sad():
print('呜呜~')
Cat.run()
Cat.set()
@classmethod
def run(cls):
print('起飞~')
@classmethod
def say(cls):
print('%s,你好!' % cls.name)
cls.set() # 类方法可以调用静态方法
@staticmethod
def stand():
print('{},站着!'.format(Cat.name))
Cat.run() # 静态方法可以调用类方法
@staticmethod
def set():
print('{},坐下!'.format(Cat.name))
def __init__(self):
print(self.name)
self.name = '胖虎'
def sleep(self):
print('呼呼~')
# 类对象
print(Cat.name) # 小敏
Cat.smile() # 哈哈~
Cat.sad() # 呜呜~ -> 起飞~ -> 小敏,坐下
Cat.run() # 起飞~
Cat.say() # 小敏,你好!-> 小敏,坐下!
Cat.stand() # 小敏,站着!-> 起飞~
Cat.set() # 小敏,坐下!
Cat.sleep() TypeError: sleep() missing 1 required positional argument: 'self'
# 实例对象
cat1 = Cat() # 小敏
print(cat1.name) # 胖虎
cat1.smile() TypeError: smile() takes 0 positional arguments but 1 was given
cat1.sad() TypeError: sad() takes 0 positional arguments but 1 was given
cat1.run() # 起飞~
cat1.say() # 小敏,你好!-> 小敏,坐下!
cat1.stand() # 小敏,站着!-> 起飞~
cat1.set() # 小敏,坐下!
cat1.sleep() # 呼呼~
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f'Vector({self.x}, {self.y})'
def __eq__(self, other):
return self.x == other.x and self.y == other.y
v1 = Vector(3, 4)
v2 = Vector(5, 6)
print(Vector(3, 4) + Vector(3, 4)) # 输出:Vector(6, 8)
print(v1 == v2) # 输出:False
- **祖先对象中包含的基本方法**
```python
print(dir(object))
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
总结
Python 中最核心的对象 object
是类 class
,定义类通常要编写类属性、普通方法、实例初始化方法、实例方法、类方法、静态方法等。把类及其实例比作函数中的主函数和子函数,那么类属性可认为是全局变量,实例属性可认为是局部变量。类的各方法间并不是彼此毫不相干,而是有着很强的共性,设计的目的是为了提升编程效率,具体使用何种方法并无绝对灵活运用即可,大可不必局限在抽象定义之中