Python 的面向对象编程
0

1. 定义类并创建类的实例

类是抽象的模板,实例是根据类创建出来的一个个具体的对象,每个对象都有相同的方法,但各自的数据可能不同。

class Person:
  pass
p1 = Person()
p2 = Person()
print(Person)
print(p1)
print(p2)

输出结果为:

<class 'main.Person'>
<main.Person object at 0x00000218F94330F0>
<main.Person object at 0x00000218F9433F60>

2. 属性

实例属性
类的每个实例可以拥有各自不同的属性,对每一个实例,都可以直接给它们设置属性并给属性赋值

p1.name = 'ren'
p2.age = 25
print(p1.name)
print(p2.age)

输出结果为:

ren
25

初始化实例属性 - 特殊方法__init__()

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age
p1 = Person('ren', 25)
p2 = Person('xiu', 26)
print(p1.name, p1.age)
print(p2.name, p2.age)

输出结果为:

ren 25
xiu 26

访问限制
如果一个属性由双下划线__xxx开头,该属性就无法被外部直接访问;

class Person:
  def __init__(self, name, age):
    self.name = name
    self.__age = age
p1 = Person('ren', 25)
print(p1.name)
print(p1.__age)

输出结果为:

ren
Traceback (most recent call last):
File "C:/Users/17966/PycharmProjects/test/test.py", line 23, in
print(p1.__age)
AttributeError: 'Person' object has no attribute '__age'

被限制访问的属性,其实是解释器对其进行了重命名(print(p1._Person__age) --> 输出:25);
如果一个属性以__xxx__的形式定义,那它又可以被外部直接访问了,这类属性被称为特殊属性,有很多Pyhton预定义的特殊属性可以使用;
以单下划线开头的属性_xxx,虽然也可以被外部访问,但按照习惯不建议这么做。

类属性
绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且所有实例访问的类属性都是同一个。也就是说,实例属性每个实例各自拥有且互相独立,但类属性有且只有一份。

class Person:
  total = 0 # 定义一个类属性(用来表示本类一共实例化的对象总个数)
  def __init__(self, name, age):
    self.name = name
    self.__age = age
    Person.total += 1
p1 = Person('ren', 25)
print('a -- ', p1.total) # 通过实例访问类属性
print('b -- ', Person.total) # 通过类之间访问类属性
p1.total = 100 # 通过实例修改类属性--不可行,事实上是给该实例重新定义了一个名叫total的实例属性
print('c -- ', Person.total) # 上一行语句未达到预期
print('d -- ', [x for x in dir(p1) if x[0] != '_']) # p1确实多出来一个叫total的实例属性
Person.total = 100 # 正确的方法是通过类之间修改类属性
print('e -- ', Person.total) # 上一行语句执行成功达到预期
Person.address = 'china' # 类属性也可以在外部动态的添加和修改
print(Person.address)

输出结果为:

a -- 1
b -- 1
c -- 1
d -- ['name', 'total']
e -- 100
china

类属性和实例属性名字冲突

通过实例访问类属性时,如果该实例有与类属性同名的实例属性,则优先访问实例属性,如果没有同名的实例属性,则访问的就是类属性;
千万不要在实例上修改类属性,它实际上并没有修改类属性,而是给实例绑定了一个新的实例属性(与期望访问的类属性同名)。

3. 方法

实例方法
实例的方法就是在类中定义的函数,在实例方法内部,可以访问实例的所有属性,包括以双下划线开头的__xxx有外部访问限制的隐藏属性。

class Person:
  def __init__(self, name, age):
    self.name = name
    self.__age = age
  def p_person(self):
    print(self.name, self.__age)
p1 = Person('ren', 25)
p1.p_person()

输出结果为:

ren 25

类方法
在类的定义中,通过在方法前标记一个@classmethod,该方法将绑定在类上,而不是类的实例上。

class Person:
  total = 0
  def __init__(self, name, age):
    self.name = name
    self.__age = age
    Person.total += 1
  @classmethod
  def how_many(cls):
    print(cls.total)
p1 = Person('ren', 25)
p2 = Person('xiu', 26)
Person.how_many() # 推荐通过类访问类方法
p1.how_many() # 通过实例也可以访问类方法

输出结果为:

2
2

4. 继承

如果已经定义了Person类,需要定义新的Student类,可以直接从Person类继承;定义Student类时只要把子类特有的新增属性加上即可。

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age
class Student(Person):
  def __init__(self, name, age, score):
    super(Student, self).__init__(name, age)
    self.score = score
s1 = Student('ren', 25, 88)
print(s1.name, s1.age, s1.score)

输出结果为:

ren 25 88

super()函数返回当前类继承的父类,通过这个返回调用的任何方法都是父类的方法;
super()函数是很智能的,即使当前类已经继承了多个父类,也只需要使用一次super()函数,在内部会进行最合理的处理;
在上边的例子中,子类的__init__()方法中,通过调用super()函数进而调用父类的__init__()方法,这个动作是必要的,否则从父类继承过来的属性会丢失掉。
子类重写超类方法
在子类中,可以重新定义父类的方法,通过子类的实例调用方法,总是先在子类自己的方法中查找,如果找不到,才会向上从父类的方法中查找。

5. 多重继承

Python允许一个父类有多个子类,或一个子类有多个父类。

class A:
  pass
class B(A):
  pass
class C(A):
  pass
class D(B, C):
  pass
a = A()
b = B()
c = C()
d = D()
print(isinstance(a, A), isinstance(a, B))
print(isinstance(b, B), isinstance(b, A))
print(isinstance(d, A), isinstance(d, B), isinstance(d, C))

输出结果为:

True False
True True
True True True

6. 特殊方法

形式__xxx__()
特点:在类中定义; 不需要直接调用(是可以这么做的); 当某些函数或操作符作用于类的实例时,会自动调用实例的特殊方法;
使用:只需要编写用到的特殊方法; 有关联性的特殊方法应该成组定义;
举例

class Student:
  def __init__(self, name,id):
    self.name = name
    self.id = id
  def __str__(self):
    return 'Student:%d %s' % (self.id, self.name)
  def __len__(self):
    return len(self.name)
  def __add__(self, other):
    return Student(self.name+other.name, max(self.id, other.id)+1)
  def __sub__(self, other):
    pass
  def __mul__(self, other):
    pass
  def __divmod__(self, other):
    pass
  def __int__(self):
    return 65535
  def __call__(self, *args, **kwargs):
    print('编不下去了。。。')
  def getname(self):
    return self.name
s1 = Student('ren', 1)
s2 = Student('xiu', 2)
print(s1) # 调用 __str__()
print(len(s1)) # 调用 __len__()
print(s1 + s2) # 调用 __add__()
print(int(s1)) # 调用 __int__()
s1() # 调用 __call__()

输出结果为:

Student:1 ren
3
Student:3 renxiu
65535
编不下去了。。。

7. 特殊属性

形式__xxx__
举例
__slots__,类属性,一个字符串列表,定义了类允许的属性名称范围,__slots__定义的属性范围限制仅对当前类实例起作用,对继承的子类是不起作用的,除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
__doc__,定义了类的文档字符串;
__dict__,一个字典,包含类的所有属性和方法;

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!