Python成为专业人士笔记-Class 类用法剖析

总体

Python不仅作为一种流行的脚本语言,而且还支持面向对象的编程范例。class类描述了数据并提供了操作数据的方法,并通过对类的对象实例化将实现层与抽象层进行分离

使用类的代码通常更易于阅读、理解和维护

类介绍

一个类,即是一个定义对象基本特征的模板。这里有一个例子:

class Person(object):
    """一个简单的类."""
    species = "Homo Sapiens" # 类属性

    def __init__(self, name):  # 特殊方法--构造方法

        """一旦基于类实例化一个对象,即会自动调用此方法
         """
        self.name = name  #  实例属性赋值,即将实例化对象的参数值赋给对象


    def __str__(self):  # 特殊方法
        """  当Python试图将对象转换为字符串时,将运行此方法。在使用print()等函数时返回该字符串 
         """
        return self.name


    def rename(self, renamed):  # 普通方法
        """ 重新分配并打印name属性"""
        self.name = renamed
        print("现在我的名字是: {}".format(self.name))

在查看上面的示例时,有几个点尤其要注意:

  1. 类由属性(变量)和方法(函数)组成
  2. 属性和方法被简单地定义为普通的变量和函数 .
  3. 正如在上面这个类中所注意到的,__init__()方法被称为初始化器。它等价于其他面向对象语言中的构造函数,是在创建新对象或类的新实例时首次运行的方法 .
  4. 在类中一开始就定义的应用于整个类的属性,这些属性称为类属性 .
  5. 应用于类的实例(对象)的属性称为对象属性。它们通常在__init__()中定义;这不是必须的,但是建议这样做(因为定义在init()之外的类全局属性在定义之前会有被访问的风险)
  6. 类定义中包含的每个方法都将自身对象作为其第一个参数传递。self用来代表这个参数(self的使用实际上是按照约定的,因为在Python中self这个词没有固有的含义,但是这是Python最受尊敬的约定之一,您应该始终遵循它)。
  7. 那些习惯于用其他语言进行面向对象编程的人可能会对某些事情感到惊讶:Python没有私有属性和方法的概念!因此默认情况下,所有内容都等价于C ++ / Java中的公共关键字

现在让我们创建Person类的几个实例 !

kelly = Person("Kelly")
joseph = Person("Joseph")
john_doe = Person("John Doe")

我们目前有三个Person对象,kelly, joseph和john doe

我们可以使用点操作符从每个实例访问类的属性。再次注意类属性和实例属性之间的区别 :

print(kelly.species)
#输出:'Homo Sapiens

print(john_doe.species)
# 输出:'Homo Sapiens 

#上面两个输出的是类属性,值一样

print(kelly.name)
#输出:'Kelly'

print(joseph.name)
#输出:'Joseph'

#上面两个是实例属性,值不一样

类继承

Python中的继承基于Java、c++等其他面向对象语言中使用的类似思想。可以从现有类派生一个新类,如下所示 :

class BaseClass(object):
    pass
class DerivedClass(BaseClass):
    pass

BaseClass是已经存在的(父)类,而DerivedClass是新的(子)类,它从BaseClass继承(或子类)属性。注意:从Python 2.2开始,所有类都隐式继承自object类,object类是所有内置类型的基类

在下面的示例中,我们定义了一个父Rectangle类,它隐式地继承自object

扫描二维码关注公众号,回复: 11306175 查看本文章
class Rectangle():
    def __init__(self, w, h):
        self.w = w
        self.h = h
 
    def area(self):
        return self.w * self.h
 
    def perimeter(self):
        return 2 * (self.w + self.h)

Rectangle 矩形类可以用作定义方形类的基类,因为正方形是矩形的一种特殊情况:

class Rectangle():
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def area(self):
        return self.w * self.h

    def perimeter(self):
        return 2 * (self.w + self.h)



class Square(Rectangle):
    def __init__(self, s):
        #调用父构造函数,w和h都是s ,即正方形宽高是相同的值
        #super(Square, self).__init__(s, s)   #python2X版本
        super().__init__(s, s)   #python3X版本
        self.s = s

a=Square(2)

print(a.area());

#输出:4

Square类将自动继承Rectangle类和object类的所有属性。super()用于调用Rectangle类的init()方法,本质上是调用基类的任何重写方法。

注意:在python3中,super()不需要参数

涉及继承类的内置函数:

issubclass(a, b): 如果a是b的子类,则返回True

isinstance(s, Class): 如果s是class的实例对象,则返回True

我们接着上面的例子:

class Rectangle():
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def area(self):
        return self.w * self.h

    def perimeter(self):
        return 2 * (self.w + self.h)



class Square(Rectangle):
    def __init__(self, s):
        #调用父构造函数,w和h都是s ,即正方形宽高是相同的值
        #super(Square, self).__init__(s, s)
        super().__init__(s, s)
        self.s = s


# 子类检查
print(issubclass(Square, Rectangle))
# 输出: True

#是否类的实例检查
r = Rectangle(3, 4)
s = Square(2)

print(isinstance(r, Rectangle))
#输出:True

print(isinstance(r, Square))
#输出:False 注意,这里r是由Rectangle实例化的对象

类动态调整

类动态调整意味着在类定义之后添加一个新变量或方法。例如,我们将类A定义为:

 class A(object):
 def __init__(self, num):
     self.num = num
 def __add__(self, other):
     return A(self.num + other.num)

但是现在我们想在稍后的代码中添加另一个函数。假设这个函数如下 :

def get_num(self):
    return self.num

但是我们如何把它作为一个方法动态添加到A中呢?这很简单,我们只需要把这个函数和赋值语句一起放到A中即可:

 A.get_num = get_num

为什么可以这样?因为函数和其他对象一样都是对象,而方法又是是属于类的函数对象,因此, get num函数对所有已经创建的实例以及基于类A的产生的新实例都可用:

foo = A(42)
A.get_num = get_num
foo.get_num() # 42 

bar = A(6);
bar.get_num() # 6

注意,与其他一些语言不同,动态调整不适用于内置的数据类型,而且也不建议这样做

类的多重继承

Python使用C3线性化算法来确定解析类属性(包括方法)的顺序。这被称为类的解析顺序(MRO)

这是一个简单的例子:

class Foo(object):
    foo = 'attr foo of Foo'


class Bar(object):
    foo = 'attr foo of Bar'  # 这个赋值是无效的
    bar = 'attr bar of Bar'


class FooBar(Foo, Bar):
    foobar = 'attr foobar of FooBar'

现在,如果我们实例化FooBar,我们查找foo属性,会看到Foo类的foo属性首先被找到

class Foo(object):
    foo = 'attr foo of Foo'


class Bar(object):
    foo = 'attr foo of Bar'  
    bar = 'attr bar of Bar'


class FooBar(Foo, Bar):
    foobar = 'attr foobar of FooBar'


fb = FooBar()

print(fb.foo)

#输出:attr foo of Foo

这是FooBar的MRO :

class Foo(object):
    foo = 'attr foo of Foo'


class Bar(object):
    foo = 'attr foo of Bar'  # we won't see this.
    bar = 'attr bar of Bar'


class FooBar(Foo, Bar):
    foobar = 'attr foobar of FooBar'


print(FooBar.mro())

输出:

可以简单地说,Python的MRO逻辑是:

  1. 深度优先(例如,先FooBar然后Foo),除非 所有类的基类(object)被子类(Bar)阻塞
  2. 这里不允许循环继承关系 : 例如,Bar不能继承自FooBar,因为FooBar是继承自Bar的。

实例变量默认值

如果变量包含一个不可变类型的值(例如字符串),那么可以像这样分配一个默认值:

 class Rectangle(object):
     def __init__(self, width, height, color='blue'):
         self.width = width
         self.height = height
         self.color = color

     def area(self):
         return self.width * self.height

 # 创建类的一些实例 
 default_rectangle = Rectangle(2, 3)
 print(default_rectangle.color) # blue

 red_rectangle = Rectangle(2, 3, 'red')
 print(red_rectangle.color) # red

类变量和实例变量

实例变量对于每个实例都是惟一的,而类变量由所有实例共享 :

class C:
    x = 2 #这个是类变量

    def __init__(self, y):
        self.y = y # 这个是实例变量,作用域只在初始化的实例对象里有效

print(C.x)  # 2
print(C.y)  # 报错:AttributeError: type object 'C' has no attribute 'y'

a=C('3')
print(a.x)  # 2
print(a.y)  # 3

列出所有类成员

dir() 函数可以用来获取一个类的成员列表 ,所有其所有属性变量、方法以及内置函数:

class C:
    x = 2 #这个是类变量

    def __init__(self, y):
        self.y = y # 这个是实例变量

print(dir(C))
#输出:(省略了"__"符号)
['class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'weakref', 'x']

今天的分享就到这里,文中代码已经过python3云环境调试运行通过,请勿转载谢谢

猜你喜欢

转载自blog.csdn.net/oSuiYing12/article/details/106201428