面向对象编程(Object-oriented Programming,简称OOP),指一种封装代码的方法。代码封装即隐藏实现功能的具体代码,仅留给用户使用的接口。面向对象编程实在面向过程编程的基础上发展来的,它比面向过程编程具有更强的灵活性和扩展性。
self
。函数无法单独使用,只能和类的对象一起使用。实际上,Python的所有变量都是对象,都具有对应的属性以及方法。
通过 class
关键字来实,格式如下
1 | class Name:#不要忘记冒号,这一行称作类头,下面的两行称作类体 |
类的实例化:
1 | Class ClassName: |
比如:
1 | class myclass: |
Python允许空类的存在,和分支、循环和函数一样,空类中要使用关键字
pass
来防止报错。
__init__()
方法在类中可以手动添加一个 __init__()
方法,它是一个特殊的类实例方法,称为构造方法或构造函数。构造方法用于创建对象时使用,每当创建一个类的实例对象时,Python解释器都会自动调用它。__init__()
方法必须包含一个名为 self
的参数作为它的第一个参数。注意
__init__
有四个下划线。
1 | class student: |
如果上面的 _init_()
方法没有任何参数,像下面这样:
1 | class student: |
运行后会报错:
1 | TypeError: student.__init__() takes 0 positional arguments but 1 was given |
而只要加上 self
参数就可以正常输出
involking construction method
,这表明在创建对象
p1
时Python解释器会自动调用 _init_()
方法并且给其传入一个参数。(传入的是什么?)
当使用其他方法时,比如上面例子中的 myfunc()
,如果
myfunc()
的定义没有参数,那么运行后会报错:
1 | TypeError: student.myfunc() takes 0 positional arguments but 1 was given |
这表明对于类中的除了构造方法的其他的每一个方法,使用它们的时候Python解释器都会自动给它们传入一个参数。
self
参数基本介绍上面的 self
参数是对类的当前实例的引用,用于访问该类的变量。它不一定要命名为
self
,但是它必须是类中任意函数的首个参数。不同的函数这一个参数可以不一样,比如上面的例子中,我们可以把
_init_()
函数的第一个参数改为 abc
,而把
myfunc
函数中的第一个参数改为 xyz
.
self
用法详解上面提到 self
参数不一定要命名为
self
,可以随便命名,其实常命名为 self
时约定俗成的一种习惯,并且它的名称self已经指明了这个参数的意义,赋予了程序更好的可读性。
同一个类可以产生多个对象,当某个对象调用类方法时,该对象会把自身的引用作为第一个参数自动传给该方法,换句话说,Python会自动绑定类方法的第一个参数指向该调用方法的对象。self
参数保证了每个对象只能调用自己的类变量和类方法。
self
表示实际调用该方法的对象,无论是构造方法还是普通的类方法,实际是谁调用的它们,第一个参数
self
就代表了谁。
self
参数其实相当于C++中的this
指针。
如果用 print
函数输出
self
,会输出该对象在计算机中存储的位置:
1 | <__main__.student object at 0x0000021C8EA0B670> |
采用的例子:
类体中、所有函数之外的范围内定义的变量叫做类属性或类变量
类变量被所有类的实例化对象所共有。可以直接用类名或者类化对象调用类变量,同时也可以使用类名调用类变量来修改它的值,不能通过类对象修改类变量的值,因为通过类对象“修改类变量”并不是在修改它,而是定义新的实例变量。比如:
1 | stu1=student() |
通过类名修改类变量的值,会影响到所有的类对象。
不推荐使用类对象来调用类变量,因为实例变量与类变量可以重名,此时无法使用类对象调用类变量而只能调用实例变量。
类体中,所有函数内部,以 self.变量名1=变量名2/值
的方式定义的变量就做实例属性或实例变量。
与类变量不同的是,实例变量不能通过类名调用,只能通过实例化对象调用。类对象只有调用了某方法,才能拥有该方法的实例变量,比如
1 | stu=student() |
修改某个类对象的实例变量之后,不会影响其他的类对象,也不会影响与之重名的类变量。
类体中、所有函数内部以 para_name=value
的方式定义的变量称为局部变量。和C/C++一样,局部变量只能在函数内部使用。
删除用 del
关键字。
最简单的区分方式:使用 @classmethod
修饰的方法为类方法,采用 @staticmethod
修饰的方法为静态方法,其余方法为实例方法。
实际编程中几乎用不到类方法和静态方法。
实例方法:通常情况下,在类中定义的方法默认为实例方法,类的构造方法也是实例方法,但是它稍微特殊一些。
实例方法必须要包含 self
参数来绑定调用此方法的类对象。
两种调用方法,类名调用实例方法和类对象调用:
1 | #方法一 |
注意:用类名调用实例方法使Python并不会自动给self参数传值,需要手动传值(除非没有self参数),且传值的内容没有限制,比如下面传入了字符串:
1 | student.myfunc("zhangsan")#传入张三 |
类方法:前面用 @classmethod
修饰,必须包含 cls
参数(当然也可以不命名为
cls
),用类名直接调用即可。
静态方法:前面用 @staticmethod
修饰,类名与类对象都可调用。
1 | def method():#定义一个函数 |
对于动态增加的方法,Python不会自动将方法调用者绑定到它们的第一个参数,因此程序必须手动为第一个参数传入参数值。
如果我们定义的新方法有参数 self
,比如
1 | def method(self,content): |
借助 types
模块下的
MethodType
,我们可以直接将 self
参数与对象绑定,再使用方法时便无需给手动给 self
传参:
1 | def method(self,content): |
继承机制经常用于创建和现有类功能类似的新类。通过继承机制,可以轻松实现类的重复使用,避免了代码的复制粘贴。
任何类都可以作为父类,创建规则与创建其他类相同。下面是一个父类:
1 | class Person: |
根据上面的父类,创建子类:
1 | class Student(Person): |
如果相比父类不做任何更改,在上面添加 pass
即可,此时子类继承了父类的所有属性与方法。
如果父类的某个方法的功能不能满足需求,可以在子类重写方法(方法名要与父类的该方法相同,类属性也有类似的操作),用新方法覆盖父类的方后,子类将不再继承父类的方法(或属性):
1 | class Student(Person): |
当子类中的某个方法覆盖了父类中同名的方法时,可以通过
父类名.方法名(参数)
来调用父类的这个方法。
如果还想继承,可以采用下面的方式:
1 | class Student(Person): |
用 super()
函数可以让子类继承父类的所有属性与方法:
1 | class Student(Person): |
单继承:子类有且只能由一个父类。Python和C++都支持多继承。
当一个子类的多个父类含有相同名字的属性与方法时,排在前面的父类会覆盖排在后面的父类的同名的属性与方法。
不建议使用多继承。
迭代器是一种可迭代的对象。
字符串、列表、元组、字典和集合都是可迭代的对象,我们可以从中获取迭代器。
iter()
方法返回一个迭代器:
1 | str="1234567" |
next()
方法返回容器内的下一个元素,两个参数,第一个参数为迭代器,第二个参数可以不设置,其作用是在没有此参数且没有下一个元素的时候抛出
StopIteration
异常;
设置第二个参数时会返回该参数(称为默认值default):
1 | #接上 |
可迭代的对象必须要含有 __iter__()
方法和
__next__
方法:
1 | class Numbers: |
不想再单独写一篇文章记录了,随手记下来吧。
1:频繁报错下面内容,常常在复制粘贴别处代码后出现:
1 | Inconsistent use of tabs and spaces in indentationPylance |
混用了Tab键和Space键的缩进(表面看上去毫无问题啊,就算删掉全部换成Tab也还是会报错……不知道具体原因是什么)
两种解决方法:
1)前面缩进正常的部分的前面的空格复制粘贴到报错的部分。
2)全选Shift+Tab,再把需要Tab的地方进行缩进。(有点麻烦
2:
1 | 'int' object is not callable |
上面的报错内容一般是函数名和变量名重复了。需要注意的是,若函数名为
f
而变量名为 F
(即相同字母的大小写),也有可能报上述错误。