• 和其他编程相比,python 在尽可能不增加新的语法和语义的情况下加入了类机制;

  • python 中的类提供了面向对象编程的所有基本功能:封装,继承,多态;


类的定义:

python 中定义类的语法如下:

class 类名:属性列表方法列表
# 定义一个类:以 class 关键字开始,后跟类名,然后以冒号结尾
class Cat:# 属性name = ""age = 0# 方法(self 后面再讲)def eat(self):print("===== 猫吃鱼")

类的对象:

类表示一类事物,比如 猫,是一类生物的统称;

而对象是类的实例,表示一个具体的事物,比如 汤姆猫,就是一个具体的生物;

类的实例化,意思就是创建一个类的对象;

python 中实例化一个对象的语法如下:

对象名 = 类名()

注意:在 python 中,没有 new 这个关键字,所以创建对象的时候,直接写类名即可;例如:

# 定义一个类:以 class 关键字开始,后跟类名,然后以冒号结尾
class Cat:pass    # pass 是一个空语句,用于保持程序结构的完整性# 实例化一个对象
tom = Cat()
print(tom)

输出结果:

类属性 和 对象属性:

在类中直接定义的属性,叫做类属性;

通过对象定义的属性,叫做对象属性(对象属性也可以叫做实例属性);

# 定义一个类
class Cat:# 类属性name = "汤姆猫"# 输出类属性的值:通过 类名.属性名 调用;
# 类属性和对象没有关系,不管实例化多少对象,
# 也不管在对象中怎么操作类属性,类属性及其值都是固定不变的
print("类属性 name:", Cat.name)# 实例化一个类的对象
cat = Cat()# 输出对象属性的值:通过 对象名.属性名 调用;
# 如果对象属性名称和类属性名称一致,并且对象属性并没有设置新的值时,
# 对象属性的值 就是类属性的值;
print("对象属性 name:", cat.name)print("==================")# 设置对象属性:
# 即使对象属性名称和类属性名称一样,为对象属性设置新值时,也不会改变类属性的值;
cat.name = "蓝猫"
# 为对象添加新的属性,该属性只在当前对象中有用;类或者别的对象,都无法操作该属性
cat.age = 22print("对象属性 name:", cat.name)     # 输出的是为对象属性设置的新值
print("对象属性 age:", cat.age)        # age 属性只有当前 cat 对象能够访问print("==================")# 再次输出类属性的值:可以发现,虽然对象属性 name 设置了新值,但是类属性 name 的值依然不变
print("类属性 name:", Cat.name)

输出结果:

总结:调用类属性的时候,最好通过 类名.属性名 调用;而调用对象属性的时候,只能通过 对象名.属性名 调用;

类方法、对象方法 和 静态方法:

类方法需要用 @classmethod 进行修饰,并且需要声明一个参数,用于接收类本身;

对象方法不需要用注解进行修饰,对象方法和普通方法的唯一区别,就是对象方法必须有一个参数,用于接收对象;而且该参数必须放在第一位;对象方法也可以叫做实例方法;

静态方法需要用 @staticmethod 进行修饰,该方法可以没有参数;

# 定义一个类
class Cat:# 类属性name = "大脸猫"# 对象方法:# 对象方法和普通的函数有一个区别:就是对象方法必须有一个额外的第一个参数,通常用 self 表示;# self 不是 python 的关键字,把它换成其他任意一个字符串都可以;# self 参数用于接收 类的对象,而 self.__class__ 则指向类本身;# 由于 self 相当于对象,那么 self.name 获取的就是对象属性的值,而不是类属性的值;def sleep(self):print(self)print(self.__class__)print(self.name + "爱睡觉")# 类方法:必须用 @classmethod 注解进行修饰;# 类方法和对象方法一样,也必须有一个额外的第一个参数,通常用 cls 表示;# cls 也不是 python 的关键字,把它换成其他任意字符串也可以;# cls 参数用于接收类的本身,那么 cls.name 获取的就是类属性的值;# 注意:类方法中不能获取到对象的属性;@classmethoddef eat(cls):print(cls)print(cls.name + "爱吃鱼")# 静态方法:必须用 @staticmethod 进行修饰,其他用法和普通函数一样;@staticmethoddef catch():print("猫会抓老鼠")# 实例化一个对象
tom = Cat()
tom.name = "汤姆猫"    # 设置对象属性的值print(tom)  # 直接输出对象# 通过对象调用类中的对象方法:
# 相当于 tom.sleep(tom),即将 tom 对象传入到对象方法中;
# 此时对象方法中的 self 参数接收到的实参就是 tom 对象;
# 通过对象调用对象方法,不需要手动传入对象参数,因为已经知道了是哪个对象调用的;
tom.sleep()# 如果通过类名去调用对象方法,必须传入一个对象参数,
# 因为对象方法中的 self 参数不知道接收的是哪个对象,所以需要手动指定对象;
Cat.sleep(tom)print("=========================")print(Cat)  # 直接输出类# 通过类名调用类方法:
# 相当于 Cat.eat(Cat),即将 Cat 类本身传入到类方法中,
# 此时类方法中的 cls 参数接收到的实参就是 Cat 类本身;
Cat.eat()
tom.eat()   # 类方法也可以通过对象来调用print("=========================")# 调用静态方法,就当成一个普通方法去调用即可;
# 静态方法通过类名或者对象去调用都可以;
Cat.catch()
tom.catch()

输出结果:

初始化方法 __init__():

初始化方法用于初始化类的对象;

# 定义一个类
class Cat:# 初始化方法,在类实例化的时候,会自动调用,用于初始化对象# 默认的初始化方法,只有一个 self 参数,用于接收对象def __init__(self):print("调用了默认的初始化方法")# 多参数的初始化方法def __init__(self, name, age):print("调用了三个参数的初始化方法")# 初始化对象属性self.name = nameself.age = age# 对象方法def eat(self):print("%s爱吃鱼" %self.name)# 实例化类:调用三个参数的初始化方法,并将参数传入到初始化方法中;
tom = Cat("汤姆猫", 33)
print("%s的年龄是%d" %(tom.name, tom.age))
tom.eat()   # 一个类可以有多个对象
lanmao = Cat("蓝猫", 22)
print("%s的年龄是%d" %(lanmao.name, lanmao.age))
lanmao.eat()

类中的 __str__() 方法:

如下代码,输出一个对象的名字:

# 定义一个类
class Cat:pass  # pass 是空语句,只是为了保持程序结构的完整性# 创建一个对象
tom = Cat()
# 输出对象
print(tom)

输出结果为:

输出的信息表示 Cat 类的对象(object),以及在内存中的地址;

如果我们想在输出对象的时候,输出我们自己想要的内容,就可以使用 __str__() 方法实现:

# 定义一个类
class Cat:# 注意:__str__() 方法必须要有返回值def __str__(self):return "自定义输出信息"# 创建一个对象
tom = Cat()
# 此时输出对象时,输出的就是 __str__() 方法返回的数据
print(tom)

类中的 __del__() 方法:

当对象的引用计数归 0 的时候调用(即对象被删除或释放的时候调用);

# 定义一个类
class Cat:# 当对象的引用计数归 0 的时候调用(即对象被删除或释放的时候调用)def __del__(self):print("===== over =====")# 创建一个对象,tom1 是该对象的引用,即 tom1 指向该对象
tom1 = Cat()# tom2 指向 tom1 所表示的对象,即 tom2 和 tom1 是同一个对象的引用
tom2 = tom1del tom1    # 删除 tom1 引用,tom2 不受影响
print("****** del *****")

输出结果:

可以看到,先输出的是 del,后输出的是 over;这是因为 Cat 类在实例化对象以后,有两个引用,tom1 和 tom2,虽然删除了一个 tom1,但是 tom2 不受影响,所以对象还存在;最后程序在运行结束的时候,所占用的内存要全部释放,此时才会调用 __del__() 方法;

如果在程序运行结束前,就把对象的引用全部删除,就会先调用 __del__() 方法了,如下所示:

# 定义一个类
class Cat:# 当对象的引用计数归 0 的时候调用(即对象被删除或释放的时候调用)def __del__(self):print("===== over =====")# 创建一个对象,tom1 是该对象的引用,即 tom1 指向该对象
tom1 = Cat()# tom2 指向 tom1 所表示的对象,即 tom2 和 tom1 是同一个对象的引用
tom2 = tom1del tom1    # 删除 tom1 引用,tom2 不受影响
del tom2    # 删除 tom2 引用,此时对象的引用计数归 0,对象被释放,调用 __del__() 方法
print("****** del *****")

输出结果:

获取对象的引用个数:

import sys   # 导入模块# 定义一个类
class Cat:# 当对象的引用计数归 0 的时候调用(即对象被删除或释放的时候调用)def __del__(self):print("===== over =====")# 创建一个对象,tom1 是该对象的引用,即 tom1 指向该对象
tom1 = Cat()# 获取对象引用的个数(要比实际引用个数多1)
print("a....", sys.getrefcount(tom1))tom2 = tom1 # tom2 指向 tom1 所表示的对象,即 tom2 和 tom1 是同一个对象的引用print("b....", sys.getrefcount(tom1))del tom2    # 删除 tom2 引用,tom1 不受影响print("c....", sys.getrefcount(tom1))del tom1    # 删除 tom1 引用,此时对象的引用计数归 0,对象被释放,调用 __del__() 方法print("d....", sys.getrefcount(tom1))   # tom1 删除后,再获取 tom1 的引用个数会报错print("****** del *****")

输出结果:

私有属性 和 私有方法:

私有属性:

# 定义一个类
class Cat:# 初始化方法:# 会覆盖掉默认的初始化方法(默认的初始化方法只有一个 self 参数),# 即在实例化对象的时候,不能不传参数了,必须传入两个参数def __init__(self, name, age):# 普通的对象属性self.name = name# 以 __ 开头的属性称为私有属性self.__age = age# 获取私有属性def getAge(self):return self.__age# 设置私有属性的值def setAge(self, age):if (age < 0):print("动物年龄不能为负数")elif (age > 100):print("你想成精啊。。")else:self.__age = age# 创建对象,并为对象属性设置初始值
tom = Cat("汤姆猫", 10)# 获取对象属性的值
print("name = ", tom.name)       # 普通属性可以直接通过对象调用
print("getAge():", tom.getAge())   # 通过方法获取私有属性的值
print("age = ", tom.__age)       # 私有属性不能直接通过对象调用

通过对象调用私有属性会报如下错误:AttributeError: 'Cat' object has no attribute '__age'

私有方法:

# 定义一个类
class Cat:# 以 __ 开头的方法称为私有方法def __catchMouse(self):print("你已经长大了,可以抓老鼠了")# 普通方法def checkAge(self, age):if (age < 0):print("不可以负生长哦")elif (age > 100):print("你已经成精了,不需要抓老鼠了")elif (age > 10):self.__catchMouse()else:print("你还小,打不过老鼠")# 创建对象
tom = Cat()
tom.checkAge(33)    # 通过对象调用普通方法
tom.__catchMouse()  # 对象不能直接调用私有方法,否则报错

输出结果:

继承:

# 定义一个父类 Animal
class Animal:# 父类中的方法def eat(self):print("==== 吃饭 ====")def sleep(self):print("==== 睡觉 ====")# 定义一个子类 Cat,继承父类 Animal;
# 只需在子类后面加一个小括号,括号中写上父类即可;
class Cat(Animal):def catch(self):print("==== 抓老鼠 ===")# 定义一个子类 Dog,继承父类 Animal;
class Dog(Animal):def bite(self):print("==== 咬人 ====")# 实例化类的对象
tom = Cat()
tom.eat()       # 子类对象可以直接调用父类中的方法
tom.sleep()
tom.catch()wangcai = Dog()
wangcai.eat()
wangcai.sleep()
wangcai.bite()

私有属性 和 私有方法 不能继承:

# 定义一个父类
class Animal:def __init__(self, name, age):self.name = name    # 公有属性self.__age = age    # 私有属性# 公有方法def eat(self):print("==== 吃饭 ====")# 私有方法def __sleep(self):print("==== 睡觉 ====")# 父类的公有方法,可以访问父类的私有属性和私有方法def test1(self):print(self.__age)self.__sleep()# 定义一个子类继承父类
class Cat(Animal):# 子类的公有方法,不能够访问父类中的私有属性和私有方法def test2(self):print(self.__age)self.__sleep()# 实例化类的对象
tom = Cat("汤姆猫", 22)print("name = ", tom.name)  # 公有属性会被继承
# print("age = ", tom.__age)  # 私有属性不会被继承(此句会报错)tom.eat()       # 公有方法会被继承
# tom.__sleep()   # 私有方法不会被继承(此句会报错)tom.test1()   # 此句可成功
tom.test2()     # (此句会报错)

多继承:

# python 中的 object 是所有类的基类,不管你写不写,都会继承于它
class Base(object):def test(self):print("======= base")# 定义一个子类 A,继承于 Base
class A(Base):def test1(self):print("====== test1")# 定义一个子类 B,继承于 Base
class B(Base):def test2(self):print("======= test2")# 多继承:一个子类继承多个父类,多个父类用逗号分隔
# C 既会继承 A 的所有公有属性和公有方法,
# 也会继承 B 的所有公有属性和公有方法;
class C(A, B):pass# 实例化类对象
c = C()
c.test()    # 子类对象调用父类的父类的方法
c.test1()   # 子类对象调用父类 A 的方法
c.test2()   # 子类对象调用父类 B 的方法

问题:如果多继承时,多个父类中有同名的方法,那么子类调用的是哪一个父类中的方法???

关于子类调用父类中方法的顺序有一个算法,叫做 C3算法,如果按照该算法表示的顺序搜索到了指定的方法,那么就停止搜索;

有一个方法可以查看搜索的顺序,该方法为:类名.__mro__(),如下代码所示:

# 基类中有一个 test 方法
class Base(object):def test(self):print("======= Base")# 父类 A 中也有一个 test 方法
class A(Base):def test(self):print("====== A")# 父类 B 中也有一个 test 方法
class B(Base):def test(self):print("======= B")# 多继承:一个子类继承多个父类,多个父类用逗号分隔
class C(A, B):pass# 实例化类对象
c = C()
c.test()    # 那么子类调用的是哪个父类的 test 方法??# 输出类 C 调用 test 方法时的搜索顺序
print(C.__mro__)

输出结果:

重写:


# 定义一个父类 Animal
class Animal:def eat(self):print("==== 吃饭 ====")# 定义一个子类 Cat,继承父类 Animal;
class Cat(Animal):# 重写父类的方法,会覆盖掉父类的方法def eat(self):print("==== 猫吃鱼 ===")# 定义一个子类 Dog,继承父类 Animal;
class Dog(Animal):def eat(self):print("==== 狗吃肉 ===")# 在重写的方法中调用父类的同名方法:方法一Animal.eat(self) # 类名.方法名(self),self 参数必须要有# 在重写的方法中调用父类的同名方法:方法二super().eat()     # 不需要传 self 方法# 实例化类的对象
tom = Cat()
tom.eat()print("**************")dog = Dog()
dog.eat()

多态:

简单点理解就是,定义一个方法的时候,并不知道调用的是哪个类中的方法;只有在执行该方法的时候,通过传入的对象,来自动识别调用的是哪个类中的方法,如下代码所示:

# 定义一个基类
class Animal(object):def eat(self):print("======= 吃饭")# 定义一个 Cat 类,继承于 Animal 类
class Cat(Animal):def eat(self):print("====== 猫吃鱼")# 定义一个 Dog 类,继承于 Animal 类
class Dog(Animal):def eat(self):print("====== 狗吃肉")# 定义一个方法,有一个用于接收对象的参数
def eat(temp):# 因为传入的对象不确定,所以具体调用哪个类中的 eat 方法,现在并不知道temp.eat()# 实例化对象
lanmao = Cat()
wangcai = Dog()# 调用普通的 eat 方法,根据传入的对象不同,调用不同类中的方法
eat(lanmao)
eat(wangcai)

对象的创建方法 __new__():

# 定义一个类
class Cat(object):# __new__() 方法用于对象的创建,但并不是调用当前类自己的 __new__() 方法创建对象,# 而是调用其父类的 __new__() 方法创建子类的对象;# 在父类的 __new__() 方法被调用的时候,子类对象还没有被创建,对象的创建是在该方法中完成的;# 在子类对象创建成功之后,__new__() 方法会返回对象的引用,然后再调用 __init__() 方法,# 将对象的引用传入进去,对对象进行初始化操作;# __new__() 方法必须声明一个额外的第一个参数,通常用 cls 表示,用于接收需要创建对象的类;# 如果在子类中重写了 __new__() 方法,就不能调用父类的该方法了,也就无法创建对象了;def __new__(cls):print("===== new")# 初始化方法,用于初始化对象,对象创建成功之后调用def __init__(self):print("===== init")# 实例化对象:
# 因为子类重写了 __new__() 方法,所有无法创建对象了,
# 也就不能调用 __init__() 方法初始化对象了
tom = Cat()# 因为对象创建失败,所有输出为 None
print(tom)

输出结果:

如果想在子类中重写 __new__() 方法,又想创建子类对象,可以在子类中显示调用父类的方法,如下所示:

# 定义一个类
class Cat(object):# 创建对象方法def __new__(cls):print("===== new")# 显示调用父类的 __new__() 方法,并将子类通过 cls 参数传入,# 对象创建之后,返回对象的引用;return super().__new__(cls)# 初始化方法def __init__(self):print("===== init")# 实例化对象:
tom = Cat()
# 输出对象
print(tom)

输出结果:

创建单例对象:

创建单例对象就是只创建一个对象,如果后面再需要对象的时候,不再创建新的,而是把之前创建好的对象拿出来用;

因为创建对象的方法是 __new__() 方法,所以可以重写该方法,实现创建单例对象:

# 定义一个类
class Cat(object):# 私有属性:用来存储实例对象__instance = None# 重写父类的 创建对象方法def __new__(cls):# 如果实例对象不存在,则创建if cls.__instance == None:# 显示调用父类的方法创建对象cls.__instance = super().__new__(cls)# 返回之前创建好的实例对象return cls.__instance# 实例化对象
cat1 = Cat()
print(id(cat1))     # 两个对象的地址一样,说明是一个对象cat2 = Cat()
print(id(cat2))

Python学习笔记(十五):python 中的面向对象相关推荐

  1. python学习笔记(十五) -- 枚举

    枚举 枚举的作用就是定义一组 数据形式为 dict 但却又不可变的常量 比如我们定义一个类,或者定义一个dict ,类中的成员变量可以被更改,而且类又可以被实例化,dict 里面的值也可以被更改. 如 ...

  2. python学习笔记十五:日期时间处理笔记

    #-*- coding: utf-8 -*- import datetime#给定日期向后N天的日期 def dateadd_day(days):d1 = datetime.datetime.now( ...

  3. python学习笔记(十六)-Python多线程多进程

    一.线程&进程 对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程, ...

  4. python复制指定字符串_python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)...

    python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...

  5. Polyworks脚本开发学习笔记(十五)-用Python连接Polyworks的COM组件

    Polyworks脚本开发学习笔记(十五)-用Python连接Polyworks的COM组件 用Polyworks脚本开发,没有高级语言的支持,功能难免单一,一些比较复杂的交互实现不了,界面和报告也很 ...

  6. python学习笔记(五岁以下儿童)深深浅浅的副本复印件,文件和文件夹

    python学习笔记(五岁以下儿童) 深拷贝-浅拷贝 浅拷贝就是对引用的拷贝(仅仅拷贝父对象) 深拷贝就是对对象的资源拷贝 普通的复制,仅仅是添加了一个指向同一个地址空间的"标签" ...

  7. 5岁自学python编程-python学习笔记(五岁以下儿童)深深浅浅的副本复印件,文件和文件夹...

    python学习笔记(五岁以下儿童) 深拷贝-浅拷贝 浅拷贝就是对引用的拷贝(仅仅拷贝父对象) 深拷贝就是对对象的资源拷贝 普通的复制,仅仅是添加了一个指向同一个地址空间的"标签" ...

  8. python学习[第十五篇] 文件系统

    python学习[第十五篇] 文件系统 对文件系统访问大多数都通过os模块实现. os 模块文件/目录访问函数 文件处理 mkfifo() 创建命名通道只用于linux remove(path)/un ...

  9. python学习笔记(五)缩进

    python学习笔记(五)缩进 原作:http://www.cnblogs.com/vamei/archive/2012/05/29/2524706.html 笔记: #!/usr/bin/env p ...

  10. python学习笔记26(python中__name__的使用)

    python学习笔记26(python中__name__的使用) 在python中,每个py文件都是一个模块,也都是一个可执行文件,即包含main方法.因此,对每个py文件,可以单独运行,也可以imp ...

最新文章

  1. 英国继银行被窃之后 信贷公司Wonga数十万客户数据被泄
  2. Containerpilot 配置文件reload
  3. tomcat中的目录映射
  4. 儿童节礼包!10 后都开始学编程了,你需要这个阿里技术电子书大全
  5. 大数据 -- 安装Hadoop-单机模式(1)
  6. java 面向对象继承的思想_Java面向对象思想!
  7. 微软低头,Chrome 一统浏览器!
  8. php应用处理数据 504,PHP与504服务器错误
  9. JAVA语言程序设计课后习题----第五单元解析(仅供参考)
  10. TeamTalk 单服务端配置
  11. 一个完整的HTTP请求过程详细
  12. 电源设计经验之 MOS 管驱动电路
  13. 4月6日——4月10日课程表
  14. python 百分号调用内置函数_建议你吃透python这68个内置函数!
  15. 国密SM2签名之公私钥及证书生成
  16. FreeRTOS 事件标志组
  17. 准备走上共享软件之路,出师不利,两块石头石沉大海,我打算流了她们。
  18. centos7 安装obs studio
  19. 信用评分卡Credit Scorecards (1-7)
  20. Hard Disk Sentinel Pro v5.70.8 硬盘哨兵 电脑硬盘检测工具

热门文章

  1. Acer 4750 安装黑苹果_10代酷睿手把手安装黑苹果
  2. Python数据分析-google play store的app数据分析
  3. python等高线绘制_用matplotlib画等高线图详解
  4. 【JVM】4种引用和使用场景
  5. 计算机培训学校可行性报告,在线考试系统可行性分析报告
  6. 记录---第一次写博客
  7. Swift游戏实战-跑酷熊猫 02 创建熊猫类
  8. vue 中使用canvas画简单的线条动画效果
  9. Android实现税博客,Android个人所得税计算器
  10. 宏基4752g 开机进度条卡到75%左右,解决办法