类体一般包含:
类的构造函数
类的构造函数在创建类的实例时自动调用,用于初始化实例的属性和执行一些必要的设置。
格式:
class ClassName:
def __init__(self, parameter1, parameter2, ...):
# 构造函数的方法体
# 使用参数初始化实例的属性等
在构造函数中,第一个参数通常是 self,表示类的实例本身,后续参数用于接收初始化时传入的值。当然你也可以把self改成其他名字
以下两个都是合法的
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
a=Person('mi',1)
print(a.name)
class Person:
def __init__(a, name, age):
a.name = name
a.age = age
a=Person('mi',1)
print(a.name)
运行结果相同
参数列表必须和__init__()的参数列表匹配,不然会出错。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
c=Person()
传值和普通的函数要求是相同的,关键字参数必须位于位置参数之后
以下写法是合法的
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
a=Person('hello',1)
b=Person("hello",age=1)
c=Person(age=1,name="hello")
print(a.name)
print(b.name)
print(c.name)
而下面的写法是错误的
class Person:
def __init__(self, name, age,gender):
self.name = name
self.age = age
self.gender=gender
a=Person(name='hello',1,'boy')
成员变量
实例变量
实例变量是属于类的实例的变量,每个类的实例都有自己的一份。
在类的方法中,通过 self
关键字来引用实例变量。
在外部通过对象名.变量名访问
比如说我们之前提到的这个例子
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
a=Person('mi',1)
print(a.name)
类变量
类变量是属于整个类而不是类的实例的变量。它在类的定义中声明,通常位于类体(class body)中,不在任何方法内。类变量的值对于该类的所有实例都是共享的。
在 Python 中,类变量可以通过以下方式定义:
class ClassName:
class_variable = value
类变量通过类名.变量名访问
如果使用实例变量名访问类变量并尝试修改它,实际上会创建一个新的实例变量,而不是修改类变量。
class Person:
classname="Person"
a=Person()
#这里访问的是类变量
print(a.classname)
#尝试通过实例修改类变量
a.classname="Man"
#实际上创建了一个叫classname的实例变量
print(a.classname)
#类变量没有被修改
print(Person.classname)
#类变量的正确修改方式
Person.classname="PERSON"
#此时a.classname访问的是实例变量而非类变量
print(a.classname)
#类变量已经被修改了
print(Person.classname)
私有化成员变量
使用双下划线 __
开头的变量被认为是私有的,这意味着它们只能在类的内部访问,而不能通过对象直接访问。这是一种封装的机制,可以防止外部直接修改类的内部状态。
这里举私有化实例变量的例子
class Car:
def __init__(self,price):
self.__price = price
def getprice(self):
print(self.__price)
c=Car(16)
print(c.__price)
直接访问是不行的,一是可以通过实例方法访问私有实例变量
class Car:
def __init__(self,price):
self.__price = price
def getprice(self):
print(self.__price)
c=Car(16)
c.getprice()
还有一种方法:可以通过_类名__变量名访问私有变量(不推荐)
class Car:
def __init__(self,price):
self.__price = price
def getprice(self):
print(self.__price)
c=Car(16)
print(c._Car__price)
属性
首先Python有个很有意思的地方
属性的创建有两种方法
类属性方式
class property(fget=None, fset=None, fdel=None, doc=None)
官网给的例子
class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
运行以下代码
class C:
def __init__(self):
self._x = 5
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
a = C()
print(a._x)
print(C.x)
print(a.x)
这里有个很有意思的的地方
而最终代码的运行结果
以上结果表明:
_x表示这是一个protected的变量,按理来说是只能在类内访问的但是Python比较开放,所以你直接在外面用实例名._x是可以直接访问的(它只会给你一个提示),还是建议大家通过类内函数来访问
x表示的是一个类属性
而通过实例来访问这个类属性会调用相应的函数
如果 c 为 C 的实例,c.x
将调用 getter,c.x = value
将调用 setter, del c.x
将调用 deleter。
装饰器方式
以下例子和上面例子等同
class C:
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
这里用了装饰器 @property
、@x.setter
和 @x.deleter
来创建一个名为 x
的属性
@property
装饰器会将 x()
方法转化为一个具有相同名称的只读属性 "getter",并将 x() 的文档字符串设为 "I'm the 'x' property"
有一个要注意的的地方
附加函数与原始的特征属性相同的名称
class C:
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def b(self):
del self._x
注意这里最后一个函数的名称最好和变量名x相同,最好是def x(self)而不是def b(self)
方法
实例方法
实例方法是定义在类中的普通方法,第一个参数是 self
,表示实例本身。
实例方法是通过实例对象调用的,它可以访问和修改实例的属性,也可以调用其他方法。
比如说在之前的例子中
class Car:
def __init__(self,price):
self.__price = price
def getprice(self):
print(self.__price)
c=Car(16)
c.getprice()
getprice就是实例方法
类方法
类方法使用 @classmethod
装饰器定义,第一个参数是 cls
,表示类本身。
类方法是通过类名调用的只能访问类变量和其他类方法
class Car:
class_name = 'class'
def __init__(self,price):
self.__price = price
def get_price(self):
print(self.__price)
@classmethod
def get_class_name(cls):
print(cls.class_name)
@classmethod
def set_class_name(cls,name):
cls.class_name = name
cls.get_class_name()
Car.set_class_name('车')
静态方法
静态方法是定义在类中的一种特殊方法,与实例方法和类方法相比,静态方法没有默认的实例或类参数。可以使用 @staticmethod
装饰器来定义静态方法。
静态方法与类无关,它们不能访问类的属性或实例的属性,它们只是依托于类,在类的某些层次执行特定的功能
私有化方法
和私有化变量类似,在变量名称前加__即可
访问私有化方法只能在类的内部访问,或者在外界通过_类名__方法名访问私有方法
通过一个题目来理解一下
以下哪句程序可以正常使用? ()
class Spam:
__ham = 0
def __eggs(self):
__ham = 100
return __ham
eggs = __eggs
s = Spam()
A. s.eggs()
B. s.__eggs()
C. s._Spam__eggs()
D. s.__ham
E. s._Spam__ham
A.s.eggs()通过调用实例方法eggs调用私有化实例方法__eggs,可以正确运行
B.s.__eggs(),我们说过不能直接访问私有方法,错误
C.s._Spam__eggs()通过_类名__方法名的方式访问是可以绕过保护机制访问私有方法的
D.不能直接访问私有实例变量,错误
E.可以通过_类名__变量名的方式访问是可以绕过保护机制访问私有变量的
类继承
子类继承父类所有的成员变量和方法,除了私有部分
包括构造方法,当子类的一个实例调用一个方法时会先检查子类里面有没有这个方法,然后如果继承了多个父类,会从左到右进行查找
以构造方法为例,子类有方法就先调用子类里的构造方法
class A:
def __init__(self,age):
print('A')
self.age=age
class B:
def __init__(self):
print('B')
class C(A,B):
def __init__(self):
print('C')
d=C()
子类没有就到父类去找
class A:
def __init__(self,age):
print('A')
self.age=age
class B:
def __init__(self):
print('B')
class C(A,B):
pass
d=C()
记住是从左到右第一个父类,而不是构造参数相同的第一个父类
交换AB位置
class A:
def __init__(self,age):
print('A')
self.age=age
class B:
def __init__(self):
print('B')
class C(B,A):
pass
d=C()
方法重写
如果父类的方法不能满足子类的要求,可以对该方法进行改写
方法重写要求父类和子类同名方法的函数签名一样,即函数名称,参数个数,参数类型相同
注意:由于python比较开放,所以你在进行override的时候子类和父类同名的方法可以参数列表不一样,但是会给你一个警告(实际上还是相当于子类的方法把从继承于父类的方法覆盖掉了,因此子类的方法怎么写都可以,不像是java那样比较严格)
例如参数个数不同会出现:
但是不会报错
如果重写的方法仍想要保留父类中的代码,可以调用super().函数名来实现
例如
class A:
def __init__(self,age):
print('A')
self.age=age
def speak(self):
print("我是A")
class B:
def __init__(self):
print('B')
class C(A,B):
def __init__(self):
print('C')
def speak(self):
super().speak()
print("我是C")
d=C()
d.speak()
这种方法尤其在构造函数中使用,有时候你构造函数不用会弹出一个警告
以下程序输出是什么结果 ()
class A:
def spam(self):
return 'A.spam'
def ham(self):
return self.spam()
class B:
def spam(self):
return 'B.spam'
class C(B, A):
pass
c = C()
print(c.spam(), c.ham())
A. TypeError: Cannot create a consistent method resolution
B. B.spam B.spam
C. B.spam A.spam
D. A.spam A.spam
答案是B。这里要注意c.ham()
:由于类 A 中有 ham
方法,而 ham
方法中调用了 self.spam()
,此时 self
指向类 C 的实例,因此调用的是类 C 中的 spam
方法,从第一个父类B中寻找同名方法,输出 'B.spam'
方法重载
复习类不得不说方法重载,但是python它不支持重载,两个相同函数名的函数定义只会后一个覆盖掉前一个
class A:
def __init__(self,age):
print('A')
self.age=age
def speak(self):
print("我是A")
class B:
def __init__(self):
print('B')
class C(A,B):
def __init__(self,age,name):
super().__init__(age)
print('C')
self.name=name
def speak(self):
super().speak()
print("我是C")
def study(self):
print("我要学习")
return True
def study(self,a):
print("我要学习")
return a
d=C(15,'wang')
print(d.study())
此处并没有调用第一个study,而是报错
多态总结
多态(polymorphism)是面向对象编程(OOP)中的一个重要概念,它允许不同类的对象对相同的消息做出不同的响应。多态性通过两种方式实现:编译时多态性(静态多态性)和运行时多态性(动态多态性)。
-
编译时多态性(静态多态性):
- 也称为方法重载(method overloading)。
- 在编译时根据函数名和参数的数量或类型来确定调用的方法。
- 编译器在编译阶段就能确定要调用的方法,因此称为静态多态
-
运行时多态性(动态多态性):
- 也称为方法重写(method overriding)。
- 在运行时根据对象的实际类型来确定调用的方法。
- 通常与继承和抽象类/接口一起使用。
- 在运行时多态性中,可以通过父类的引用指向子类的对象(向上转型),然后调用相同的方法,但具体执行的是子类的方法
怎么说呢,python它既不能方法重载,它又没有向上转型,这个多态只能说如有。
一些其它类
根类:Object
在Python中,object
是所有类的基类。虽然 Python 不像 Java 和 C# 那样显式地声明类的基类,但在没有显式继承其他类时,默认继承自 object
。
object
类在 Python 中提供了一些特殊方法),例如:
__str__(self)
: 返回对象的字符串表示。__eq__(self, other)
: 定义对象的相等性。
枚举类:
from enum import Enum
class Day(Enum):
SUNDAY = 1
MONDAY = 2
TUESDAY = 3
WEDNESDAY = 4
THURSDAY = 5
FRIDAY = 6
SATURDAY = 7
today=Day.SATURDAY
print(today)
print(today.name)
print(today.value)
比较两个对象是否相同
比较值相同:重写后的def __eq__(self,other)和 ==
每一个类中都可以重写object中的__eq__方法来定义比较同一个类中不同的对象,可以自定义比较规则,一般用于比较值相同,在执行obj1==obj2时会调用该函数
注意与Java的区别,注意这个不是equals函数