一、面对对象
# 名称使用大驼峰命名法 class Cat: def eat(self): print("小猫爱吃鱼") def drink(self): print("小猫要喝水") # 创建猫对象 cat = Cat() cat.eat() # 小猫爱吃鱼 cat.drink() # 小猫要喝水 print(cat) # <__main__.Cat object at 0x000001E68791A160> # 创建多个猫对象 lazy_cat = Cat() lazy_cat.eat() lazy_cat.drink() print(lazy_cat) # <__main__.Cat object at 0x00000171CC285F40> lazy_cat2 = lazy_cat print(lazy_cat2) # <__main__.Cat object at 0x00000171CC285F40> # 将属性添加到类外,但不推荐 cat.name = "Tom"
二、在初始方法内定义属性 在****内部使用方法 可以定义属性。定义属性后,再使用Cat创建类别的对象有这个属性。
# 在初始方法内定义属性 class Cat: # 该方法在创建对象时自动调用 def __init__(self): # 该方法专门用于定义一个类的属性 print("这是一种初始化的方法") # 用 Cat 所有类创建的猫对象都有一个 name 该属性的初始值为Tom' self.name = 'Tom' def eat(self): print("%s 爱吃鱼" % self.name) # 使用类名创建对象时,自动执行:1分配空间-创建对象;2设置对象的初始值-初始化方法 tom = Cat() # 这是一种初始化的方法 tom
.eat
(
)
# Tom 爱吃鱼
在以上的案例中有个缺陷,就是不管创建多少只猫,猫的name属性都是”Tom“。为了提高灵活性,对初始化方法进行改造,初始化的同时设置初始值,这里的初始化方法可以理解为Java里类的构造函数。
# 利用参数设置属性初始值
class Cat:
# 在创建对象时自动调用该方法
def __init__(self, name):
# 该方法专门用来定义一个类具有哪些属性
print("这是一个初始化方法")
# 用 Cat 类创建的猫对象都有一个 name 属性,且该属性的初始值为'Tom'
self.name = name
def eat(self):
print("%s 爱吃鱼" % self.name)
# 在使用类名创建对象时,会自动执行:1分配空间-创建对象;2为对象设置初始值-初始化方法
tom = Cat("Tom")
tom.eat()
lazy_cat = Cat("大懒猫")
lazy_cat.eat()
三、内置方法__del__和对象的生命周期
# 利用参数设置属性初始值
class Cat:
# 在创建对象时自动调用该方法
def __init__(self, name):
self.name = name
def eat(self):
print("%s 爱吃鱼" % self.name)
def __del__(self):
print("这是一个销毁方法")
# 在使用类名创建对象时,会自动执行:1分配空间-创建对象;2为对象设置初始值-初始化方法
tom = Cat("Tom")
tom.eat() # Tom 爱吃鱼
lazy_cat = Cat("大懒猫")
lazy_cat.eat() # 大懒猫爱吃鱼
# 若这里删除了tom变量 则会在删除时就调用__del__方法
del tom # 删除tom变量后会自动调用__del__方法 输出“这是一个销毁方法”
print("-" * 30) # 这条代码是在执行 __del__ 前执行,因为 lazy_cat 是全局变量
# 这里才再次输出“这是一个销毁方法”
# __del__ 是在整个程序执行结束后才调用
四、__str__内置方法 在 python 中,用 print 打印对象变量,默认会输出以及。如果想用 print 方法打印时,就可以使用****内置方法。__str__方法必须返回一个字符串。 此方法类似于Java中重写toString()方法。
五、封装 将属性和方法封装到一个抽象类中,外界使用类创建对象,让对象调用方法,对象方法的细节都被封装到类的内部, 案例:小明爱跑步 需求: 1.小明体重75.0公斤 2.小明每次跑步会减肥0.5公斤 3.小明每次吃东西体重增加1公斤
# 封装
# 属性:姓名name、体重weight
# 方法:跑步run、吃东西eat
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):
print("%s 吃东西" % self.name)
self.weight += 1.0
def __str__(self):
return "%s 体重 %.1f 公斤" % (self.name, self.weight)
person1 = Person("小明", 75.0)
person1.run()
person1.eat()
person1.eat()
person1.eat()
person1.run()
print(person1)
# 小明 跑步
# 小明 吃东西
# 小明 吃东西
# 小明 吃东西
# 小明 跑步
# 小明 体重 77.0 公斤
六、私有属性和方法 类的私有属性和方法是在属性或方法名前添加,声明该属性和方法为私有,无法从外部直接访问该属性和方法,但在类内部的方法可以访问该类的私有属性和方法。
class Women:
def __init__(self, name):
self.name = name
self.__age = 18
def secret(self):
# 在对象内部的方法可以正常访问对象的私有属性
print("%s 的年龄是 %d" % (self.name, self.__age))
xiaofang = Women("小芳")
# 无法直接从外部访问到对象的私有属性
# print(xiaofang.__age) # AttributeError: 'Women' object has no attribute '__age'
# 私有方法也无法直接从外部访问
xiaofang.secret()
在python中,其实并没有真正意义上的私有,本质上是解释器对名称做了一些特殊的处理,使得无法从外部直接访问。若还是想访问,则可以通过**_类名__属性名/方法名**去访问。
# 私有属性 外部无法直接访问
print(xiaofang._Women__age) # 18
# 私有方法 外部不能直接调用
print(xiaofang._Women__secret()) # 小芳 的年龄是 18
七、继承 1、继承的概念:继承可以实现代码的重用,相同的代码不需要重复的编写。继承就是子类拥有父类的所有属性和方法。 2、继承的语法:class 类名(父类名): 3、继承的特性:传递性,子类既继承父类的属性和方法,还继承父类的父类的属性和方法。
八、方法重写 子类拥有父类的所有属性和方法,可以直接使用父类中已经封装好的方法,不需要再次开发。但如果父类的方法实现不能满足子类需要时,子类可以对父类的方法进行重写。
重写父类方法时有两种情况: 1、覆盖父类的方法:如果在实际开发中,子类的方法实现和父类的方法实现完全不同,就可以使用覆盖的方式。 2、对父类方法进行扩展:如果在开发中,子类的方法实现包含父类的方法实现,就可以使用扩展的方式。在子类中重写父类的方法:super().父类方法,也可以使用父类名调用父类方法:Dog.父类方法(self)。
九、父类的私有属性和方法 子类对象不能在方法内部直接访问父类的私有属性和方法。子类对象可以通过父类的公有方法间接访问父类的私有属性和方法。
十、多继承 在python中,子类可以拥有多个父类,并且具有所有父类的属性和方法。 注意:父类之间不要有重名的方法或属性。如果父类之间存在同名的属性或方法应该尽量避免使用多继承。 若父类之间出现同名的方法或属性,如何才能确定子类对象调用方法的执行顺序呢? 使用确定子类对象调用方法的顺序
class A:
def test(self):
print("这是A类的 test 方法")
def demo(self):
print("这是A类的 demo 方法")
class B:
def demo(self):
print("这是B类的 demo 方法")
def test(self):
print("这是B类的 test 方法")
# 多继承
class C(A, B):
pass
# 创建子类对象
c = C()
c.test() # 这是A类的 test 方法
c.demo() # 这是A类的 demo 方法
# 确定 C 类对象调用方法的顺序
print(C.__mro__)
# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
十一、新式类和经典类 object 是 python 为所有对象提供的基类,提供一些内置的方法和属性,可以使用 dir 函数查看。 新式类:以为基类的类 经典类:不以为基类的类 为了保证编写的代码能同时在 python2.x 和 python3.x 运行,在定义类时如果没有父类,建议统一继承 object 。
十二、多态 1、多态的概念 多态:不同的子类对象调用相同的父类方法,产生不同的执行结果。 多态是以继承和重写父类方法为前提。
class Dog(object):
def __init__(self, name):
self.name = name
def game(self):
print("%s 简单玩耍" % self.name)
class XiaoTianQuan(Dog):
def game(self):
print("哮天犬在天上玩耍")
class Person:
def __init__(self, name):
self.name = name
def game_with_dog(self, dog):
print("%s 和 %s 玩" % (self.name, dog.name))
dog.game()
# wangcai = Dog("小黑")
wangcai = XiaoTianQuan("旺财")
xiaoming = Person("小明")
xiaoming.game_with_dog(wangcai)
2、类属性和类方法 (1)类是一个特殊的对象 python中一切皆对象: : 定义的类属于 : 属于 在程序运行时,类同样会被加载到内存,,使用一个类可以创建出。除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法,即类属性和类方法,通过**类名.**的方式可以访问类的属性或调用类的方法。 (2)类属性和实例属性 类属性就是给类对象中定义的属性,通常用来记录与这个类相关的特征,不会用于记录具体对象的特征。
class Tool(object):
# 定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 针对类属性做一个计数
Tool.count += 1
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("锤子")
# 类属性的获取机制:1.类名.类属性 2.对象.类属性(向上查找机制)
print(Tool.count) # 3
print(tool1.count) # 3
注意:如果使用 赋值语句,只会给对象添加一个实例属性,而不会影响到类属性的值。 (3)类方法 类方法就是针对类对象定义的方法,在类方法内部可以直接访问类属性或调用其他类方法。
# 语法:
# 类方法需要用修饰器 @classmethod 来标识
# 类方法的第一个参数是cls,和实例方法的第一个参数self类似
# 通过类名.调用类方法,调用方法时,不需要传递cls参数
@classmethod
def 类方法名(cls):
# 在方法内部可以通过cls.访问类属性或其他类方法
print("工具对象总数为 %d" % cls.count)
pass
# 通过类名.调用类方法
Tool.类方法名()
(4)静态方法 在开发中,如果需要在类中封装一个方法,这个方法既不需要访问实例属性或调用实例方法,也不需要访问类属性或调用类方法,则可以把这个方法封装为静态方法。
# 语法:
# 静态方法需要用修饰器 @staticmethod 来标识
# 通过 类名. 调用静态方法
@staticmethod
def 静态方法名():
pass
(5)总结 实例方法 – 方法内部需要访问实例属性 类方法 – 方法内部需要访问类属性 静态方法 – 方法内部不需要访问实例属性和类属性 若在方法内部既要访问类属性又要访问实例属性,那应该定义成什么方法? 定义成实例方法,因为类只有一个,在实例方法内部可以使用类名.访问类属性。