Python的类(class)

引子

假设你现在是一个生产各种动物玩具工厂的厂长,现在有家订货商急需一批小狗玩具,于是你和工程师们连夜设计小狗模具,终于在一周后设计好了模具,并开始生产。没过多久,又有家订货商急需一批小猫的玩具,于是你和工程师们又连夜设计,终于在一周后设计好了猫模具,并开始生产。于是,你开始想,每次从头开始设计模具有点太花时间了,而且你发现小狗和小猫的某些部位是可以通用的,那么能不能制造一个通用模具,比如身体、尾巴、腿等,这样每次设计新的动物模具时,只需设计每种动物特有的部位就可以了,其他的部位可以使用通用部分,最后组装起来就可以了。这样的话,省时省力,又可以满足要求。但是新的问题又出现了,有家订货商需要一批鲨鱼玩具,你发现鲨鱼和猫、狗的玩具几乎没有通用的部分,但是和鲤鱼、鲫鱼等有相似的部分,于是你又想到,可以把这些动物进行分类,把猫、狗、猪、狮子等陆地哺乳类动物列为一类,它们可以共享一部分相同的部位,而把鲤鱼、鲨鱼等鱼类列为一类,它们可以共享一部分相同的部位。这样,我们可以设计不同类动物的模具,生产通用的部位,然后根据每种动物的特点选择不同类的模具,接着在设计每种动物的特有部分,最后组装起来就可以了。

由此我们得到一个“类”的概念。类,可以通俗地理解为一类相似对象的统称或集合,为一个抽象概念,即对象的类型。程序设计中的“类”,相当于一个通用模具,类的组成就是对该类相似对象的一个描述。对象的描述主要从两方面进行刻画,一是对象的特征(属性),二是对象的行为(方法)。创建类就创建了一个相似对象的模具,实例化就是利用该模具定制了一个具体的对象(物品)。

Python是一种面向对象编程(OOP)的语言。所谓的面向对象编程就是把对象作为程序的基本单元,一个对象包含数据特征和操作数据的函数。在Python中,所有数据类型都被视为对象,也可以自定义对象。自定义对象数据类型就是面向对象中类的概念。

下面,我们将就Python中“类”的概念进行详细说明。

1 类的构成

  • (1)类由三个部分构成 - 类名称:类型 - 类属性:对象的属性 - 类方法:对象的方法
  • (2)创建和使用类,类定义形式(代码): class 类名:
属性列表:
方法列表:
  • (3)类是对象的类型,具有相同属性和行为事物的统称。类是抽象的,在使用的时候需要定义类的一个具体存在,即实例化对象。
# 定义类
class People:
    # 类属性
    sex = 'nan'

    # 构造函数:魔术方法
    def __init__(self, name, age):
        # 实例化属性
        self.name = name  # self代表对象本身
        self.age = age

    # 实例化方法
    def sleep(self):
        self.aa = 1
        print('{}正在睡觉,性别为{}'.format(self.name, People.sex))

    def bb(self):
        print(self.aa)

# 实例化对象
people1 = People('张三', 21)  # 属性赋予实例化对象
People.sex='男'
people1.sleep()  # 调用实例化对象方法
print(people1.age)  # 可以直接打印实例化对象的属性

输出结果:

张三正在睡觉,性别为男
21

2 类的属性与实例属性

  • (1)类变量:类变量在整个实例化的对象中是公用的。
  • (2)类变量定义在类中且在函数体之外
  • (3)类变量通常不作为实例变量使用。类属性使用类变量表示的,在类的外部使用,需用类名.类属性。

直观地,类属性很好理解,类本质上就是如果我们设计的某一类物品的一个模具,类属性是这一类事物所共有的特征(属性)。

class PeopleMan:
    # 定义基本类属性
    name = ''
    age = 0
    sex = 'woman'
    __weight = 0

    def __init__(self, n, a, w):
        # 定义实例属性
        self.name = n 
        self.age = a
        self.__weigth = w

    def speak(self):
        print("%s 说:我%d 岁。" %(self.name, self.age))


p = PeopleMan('runoob', 10, 30)  # 实例化类
print(PeopleMan.sex)  # 类名.类变量  调用类变量

输出结果:

woman

  • (4)实例属性:定义在方法中的变量,只作用于当前实例的类
  • (5)变量名 为私有属性,即该类所私有的属性,普通调用方式无法调用。如果要在类外部调用,需要用 对象名._类名__私有属性 这样才能调用。注意类名前是单下划线,类名后是双下划线。
class PeopleMan:
    # 定义基本类属性
    name = ''
    age = 0
    sex = 'woman'

    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0

    def __init__(self, n, a, w):
        # 定义实例属性
        self.name = n
        self.age = a
        self.__weight = w

    def speak(self):
        print("%s 说:我 %d 岁。" % (self.name, self.age))

# 实例化类
p = PeopleMan('runoob', 10, 30)
print(p._PeopleMan__weight)  # 对象名.__类名__私有属性  这样才能调用p.__weight,否则无法调用
p.speak()

输出结果:

30
runoob 说:我 10 岁。
  • (6)可以实例化对象名+.来访问对象的属性,也可以使用一下函数的方式来访问属性(反射)
  • getattr(obj, name[,default]): 访问对象的属性
  • hasattr(obj, name): 检查是否存在一个属性
  • setattr(obj, name, value): 设置一个属性。如果属性不存在,会创建一个新属性
  • delattr(obj, name): 删除属性值,没有删除属性
  • name 需加单引号,obj为实例化对象名称
class PeopleMan:
    # 定义基本类属性
    name = ''
    age = 0
    sex = 'woman'

    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    def __init__(self, n, a, w):
        # 定义实例属性
        self.name = n
        self.age = a
        self.__weight = w

    def speak(self):
        print("%s 说:我 %d 岁。" %(self.name, self.age))

# 实例化
p = PeopleMan('runoob', 10, 30)  # p为实例化对象
getattr(p, 'sex')  # 访问对象的属性

输出结果:

'woman'
  • (7)__dict__: 类的属性(包括一个字典,由类的属性名:值组成)实现方式:实例化类名.dict
  • (8)__doc__: 类的文档的字符串 实现方式:(类名.)实例化类名.doc
  • (9)__name__: 类名 实现方式:类名.name
  • (10)__bases__: 类的所有父类构成元素(包含了一个由所有父类组成的元组
class PeopleMan:
    '''定义人类,其中有3个属性,两个方法'''
    # 定义基本类属性
    name = ''
    age = 0
    sex = 'woman'

    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0

    def __init__(self, n, a, w):
        # 定义实例属性
        self.name = n
        self.age = a
        self.__weight = w

    def speak(self):
        print("%s 说:我 %d 岁。" %(self.name, self.age))

# 实例化类
p = PeopleMan('runoob', 10, 30)  # p为实例化对象
print(getattr(p, 'sex'))  # 访问对象的属性
print(p.__dict__)
print(PeopleMan.__dict__)
print(p.__doc__)  # 类文档中的字符串
print(PeopleMan.__doc__)  # 类文档中的字符串
print(PeopleMan.__name__)

输出结果:

woman
{'name': 'runoob', 'age': 10, '_PeopleMan__weight': 30}
{'__module__': '__main__', '__doc__': '定义人类,其中有3个属性,两个方法', 'name': '', 'age': 0, 'sex': 'woman', '_PeopleMan__weight': 0, '__init__': <function PeopleMan.__init__ at 0x000002C3EAFAF5E0>, 'speak': <function PeopleMan.speak at 0x000002C3EAFAF700>, '__dict__': <attribute '__dict__' of 'PeopleMan' objects>, '__weakref__': <attribute '__weakref__' of 'PeopleMan' objects>}
定义人类,其中有3个属性,两个方法
定义人类,其中有3个属性,两个方法
PeopleMan
  • (11)尽量把需要用户传入的属性作为实例属性,而把同类都有的属性作为类属性
  • (12)实例属性在每实例化一个类时都会初始化一遍
  • (13)不同的实例的实例属性可能不同,不同实例的类属性都相同
  • (14)实例属性
  • 在__init__(self,...)中初始化
  • 内部调用时都需要加上self
  • 外部调用时用“对象名.属性名”调用
  • (15)类属性
  • 类属性可以在__init__中修改
  • 在内部用类名.类属性名调用
  • 外部既可以用类名.类属性名又可以用instancename.类属性名调用
  • (16)私有属性
  • 双下划线__开头:外部不可通过“对象名.属性名”来访问或者更改,可以通过“对象._类名__属性名”访问和更改
class PeopleMan:
    # 定义基本类属性
    name = ''
    age = 0
    sex = 'woman'
    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0

    def __init__(self, n, a, w):
        # 定义实例属性
        self.name = n 
        self.age = a 
        self.__weight = w

    def speak(self):
        print("%s 说:我 %d 岁。" %(self.name, self.age))

# 实例化类
p = PeopleMan('runoob', 10, 30)
p._PeopleMan__weight = 50 
print(p._PeopleMan__weight)

数据结果:

50

3 __init__()方法和self

  • (1)__init__()是一个特殊的方法属于类的专有方法,被称为类的构造函数或初始化方法,方法的前面和后面都有两个下划线,这时为了避免Python默认方法和普通方法发生名称的冲突。
  • (2)每当创建类的实例化对象的时候,__init__()方法都会默认被运行,作用就是初始化已实例化的对象。
  • (3)在方法的定义中,第一个参数self是必不可少的。类的方法和普通的函数区别就是self,self并不使Python关键字,完全可以用其他单词取代它,只是按照惯例和标准的规定,推荐使用self。
class PeopleMan:
    # 定义基本类属性
    name = ''
    age = 0
    sex = 'woman'
    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0

    def __init__(r, n, a, w):  # self换成r
        # 定义实例属性
        r.name = n 
        r.age = a 
        r.__weight = w

    def speak(self):  # 这个self没有换,它和上面的r一样,都是实例化对象p,该方法里的所有self也可以换成r
        print("%s 说:我 %d 岁。" %(self.name, self.age))

# 实例化类
p = PeopleMan('runoob', 10, 30)  # 创建实例化对象,p是实例化对象,默认运行__init__()
getattr(p, 'sex')  # 访问对象的属性
'woman'
  • (4)在类中方法里面,不写__init__()构造方法,直接写实例化方法,或者实例化方法里面初始化实例属性,当类比较小或只构造一个类,然后实例化方法通过实例化对象的参数传进去好像比较简单、快捷,并且不会报错,但如果类里面的多个方法需要这个参数,那么就需要传多次,比较繁琐。
  • (5)如果通过构造方法,在第一次实例化对象的时候传入,实例化可以直接调用实例化对象中的self.属性,不需要在调用实例化方法的时候传入属性,工程庞大的话,用__init__()方法然后self调用方便些,也规范些。
class PeopleMan:
    # 定义基本类属性
    name = ''
    age = 0
    sex = 'woman'
    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0

    def speak(self, n, a):  # 这个self没有换,它和上面的r一样,都是实例化对象p,该方法里的所有self也可以换成r
        self.name = n
        self.age = a
        print("%s 说:我 %d 岁。" %(self.name, self.age))

    def run(self, n, a):  # 这个self没有换,它和上面的r一样,都是实例化对象p,该方法里的所有self也可以换成r
        self.name = n
        self.age = a
        print("%s 说:我 %d 岁。" %(self.name, self.age))

# 实例化类
p = PeopleMan()  # 创建实例化对象,p是实例化对象,默认运行__init__()
p.speak('runoob', 10)   # 调用方法时传入
p.run('runnoob', 10)  # 由于实例属性没有初始化,因而如果再次调用,就需要再传入参数
runoob 说:我 10 岁。
runnoob 说:我 10 岁。

4 类的三大特性

4.1 继承

  • (1)现实生活中,继承一般指子女继承父辈的财产。程序中,继承描述的是事物之间的所属关系,例如,猫和狗都属于动物,程序中可以描述为猫和狗都继承自动物。同理,波斯猫和家猫都继承猫。而斑点狗,泰迪都继承狗。
  • (2)程序中我们定义一个class的时候,可以从某个现有的class继承,新的class称为之子类(subclass),而被继承的class称之为基类、父类或超类。
  • (3)子类继承其父类的所有属性和方法,同时还可以定义自己的属性和方法。
  • (4)可以多重继承,但是最好只写一个基类,需要注意圆括号基类的顺序,若是基类中有相同的方法名,而在子类使用时未指定,python从左至右搜索,即方法在子类中未找到时,从左至右查找基类中是否包含方法。
继承的形式:
class DerivedClassName(BaseClassName1, BaseClassName2, BaseClassName3):          
      ......   
  • (5)继承会继承父类的所有属性,如果不想被子类继承,就把属性设置为私有属性,子类无法继承父类的私有属性,但是可以访问。通常父类的基本属性对子类继承是没有影响的,通过实例化属性都是放在方法中,子类可以通过方法重写来实现对继承的属性修改。
class PeopleMan:
    # 定义基本属性
    name = ''
    age = 0 
    sex = 'woman'

    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0

    def __init__(self, n, a, w):
        # 定义实例属性
        self.name = n
        self.age = a 
        self.__weight = w 

    def speak(self):
        print("%s 说:我 %d 岁。" %(self.name, self.age))


class speaker:
    topic = ''
    name = ''
    def __init__(self, n, t):
        self.name = n 
        self.topic = t 

    def speak(self):
        print("我叫 %s: 我是一个演说家,我演讲的主题是 %s" %(self.name, self.topic))

class student(PeopleMan, speaker):  # student类拥有基类的属性和方法
    grade = ''

    def __init__(self, n, a, w, g, t):
        # 调用父类的构造函数
        PeopleMan.__init__(self, n, a, w)  # 调用父类构造方法,或者super(student, self).__init__(n, a, w)  要先传入第一个基类的参数
        speaker.__init__(self, n, t)  # 和父类实例化属性保持一致 或者super(student, self).__init__(n, t)
        self.grade = g

    def speak(self):  # 重写父类的方法,相当于对父类的方法重新定义
        print("%s 说:我 %d 岁了,我在读%d年级"%(self.name, self.age, self.grade))  # 子类可以用父类的属性


s = student('ken', 10, 60, 3, "Python")
s.speak()  # 先找子类,子类没有则从左至右基类中找方法运行结果

输出结果:

ken 说:我 10 岁了,我在读3年级

4.2 多态

  • (1)它是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。
  • (2)多态是指一类事物有多种形态,比如动物类,可以有猫、狗、猪等等。(一个抽象类有多个子类),因而多态的概念依赖于继承)。
import abc

class Animal(metaclass=abc.ABCMeta):  # 同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class Cat(Animal):  # 动物的形态之一:猫
    def talk(self):
        print('say miaomaio')

class Dog(Animal):  # 动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal):  # 动物的形态之三:猪
    def talk(self):
        print("say aoao")
  • (3)多态与多态性是两种概念,多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接受时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
  • (4)综合可以说,多态性是:一个接口,多种实现。
# 多态性
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class Cat(Animal): 
    def talk(self):
        print('say miaomiao')     # 多态性

class Dog(Animal): 
    def talk(self):
        print('say wangwang')     # 多态性

class Pig(Animal): 
    def talk(self):
        print('say aoao')         # 多态性

c = Cat()
d = Dog()
p = Pig()

def func(obj):
    obj.talk()

func(c)    # 一个接口,多种实现
func(d)    # 一个接口,多种实现
func(p)    # 一个接口,多种实现

输出结果:

say miaomiao
say wangwang
say aoao

4.3 封装性

  • (1)“封装”就是将对象的特征(属性)和行为(方法)封装在一起,形成一类对象,即类;封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只要通过外部接口,即特定的访问权限来使用类的成员。

5 类中的方法

Python中有三种方法,实例方法、静态方法(staticmethod)和类方法(classmethod)

Class A():
    method = 'class'
    
    # 实例方法
    def normethod(self):
        print('I am the normal method')

    # 静态方法
    @staticmethod
    def stamethod():
        print(' I am the static method')

    # 类方法
    def clsmethod(cls):
        print(f' I am the {cls.method} method')
    

5.1 实例化方法

实例方法第一个参数是self,它表示实例化后类的地址id,所以需要一个类的实例,才能调用这个函数。

案例一:

A.normthod()    # 直接这样调用会报错
# 必须先进行实例化
a = A()
a.normethod()   # 需要实例化后传递id参数才能调用
# 输出:I am the normal method

案例二:

class A:
    # 基本属性
    d = 0

    # 实例化方法
    def foo(self, x):
        print("executing foo(%s, %s)" %(self, x))  # 打印self, x分别是什么
        print("self: ", self)   # 打印self是什么

# 方法一:直接调用类的实例化方法
a = A.foo('a', 'b')  # self被作为一个参数传入了,self就是一个参数而已,并不是一个对象,因此不能打印self.x

# 方法二:实例化方法
b = A()  #需要用实例化对象调用它
b.foo('a')  # 打印出来的self为实例化对象的地址

输出结果:

executing foo(a, b)
self:  a
executing foo(<__main__.A object at 0x000002C3EB3579A0>, a)
self:  <__main__.A object at 0x000002C3EB3579A0>

5.2 静态方法

静态方法类似普通函数,参数里面不用传递self。有一些方法和类相关,但是又不需要类中的任何信息,出于对代码的理解和维护,就可使用静态方法。总结如下:

  • (1)通过装饰器@staticmethod装饰
  • (2)不能访问实例属性
  • (3)参数不能传入self
  • (4)与类相关但是不依赖类与实例的方法
  • (5)静态方法不需要接受参数,使用类名.类属性

案例一:

A.stamethod()    # 静态方法不用实例可直接调用
# 输出: I am the static method
a = A()
a.stamethod()   # 也可以实例化后再调用
# 输出:I am the static method

案例二:

class A:
    # 基本类属性
    d = 0

    # 静态方法
    @staticmethod
    def static_foo(x):
        print("executing static_foo(%s)" %x)
        print(A.d)  # 访问类属性,不能访问实例属性

# 静态方法
A.static_foo('a')

输出结果:

executing static_foo(a)
0

静态方法最大的优点是能节省开销,因为它不会绑定到实例对象上,它在内存中只生成一个。

a1 = A()
a2 = A()
a1.stamethod
# 输出:<function static at 0x0000000004938E48>
a2.stamethod
# 输出:<function static at 0x0000000004938E48>
A.stamethod
# 输出:<function static at 0x0000000004938E48>

a1.stamethod is A.stamethod and a2.stamethod is A.stamethod
# 输出:True

而实例方法每个实例对象都是独立的,开销较大。

a1.normethod
# 输出:<bound method A.norstatic of <__main__.A instance at 0x0000000004590AC8>>
a2.normethod
# 输出:<bound method A.norstatic of <__main__.A instance at 0x00000000045901C8>>
a1.normethod is a2.normethod
# 输出:False

5.3 类方法

类方法与实例方法类似,但是类方法传递的不是实例,而是类本身。当需要和类交互而不需要和实例交互时,就可以选择类方法。总结如下:

  • (1)在类方法前加装饰器@classmethod
  • (2)不能访问实例属性
  • (3)参数必须传入cls(即代表类对象--区别--self代表实例对象),并且用此来调用类属性:cls.类属性名
  • (4)静态方法和类方法都可以用类或者实例来调用,二者的特点是均不能调用实例属性。

案例一:

A.clsmethod()    #类方法不用实例化可直接调用
# 输出:I am the class method
a = A()
a.clsmethod()    #也可以实例化后再调用
# 输出:I am the class method

案例二:

class A:
    # 基本类属性
    d = 0

    def __init__(self, n):
        self.name = n


    # 类方法
    @classmethod
    def class_foo(cls, x):
        print("executing class_foo(%s, %s)" %(cls, x))
        print('cls: ', cls)  # 打印cls是什么
        print(cls.d)  # cls为类,cls.d为类属性
        # print(cls.n)  # 报错,因为n是实例属性,不是类的属性

# 方法一:通过类来调用
A.class_foo('a')  # cls是一个类,类方法可以只传入一个参数x来调用

# 方法二:通过实例化对象来调用
b = A("tom")
b.class_foo("a")

输出结果:

executing class_foo(<class '__main__.A'>, a)
cls:  <class '__main__.A'>
0
executing class_foo(<class '__main__.A'>, a)
cls:  <class '__main__.A'>
0
  • (5)当把装饰器cls注释掉,cls不是类,是实例化对象,相当于self
class A:
    # 基本类属性
    d = 0

    # 实例化方法
    def foo(self, x):
        print("executing foo(%s, %s)" %(self, x))
        print("self:", self)  # 打印self是什么

    # 类方法
    # @classmethod
    def class_foo(cls, x):  # cls相当于self
        print("executing class_foo(%s, %s)" %(cls, x))
        print('cls: ', cls)
        print(cls.d)  # d是类变量,类变量通常不作为实例变量使用。如果需要用在函数中使用 类名.类属性,这里用self.d可以用行,但是不规范

    # 静态方法
    @staticmethod
    def static_foo(x):
        print("executing static_foo(%s)" %x)


b = A()
b.class_foo("a")
executing class_foo(<__main__.A object at 0x000002C3EB17F520>, a)
cls:  <__main__.A object at 0x000002C3EB17F520>
0
class A:
    # 基本类属性
    d = 0

    def __init__(self, m, n):
        self.m = m
        self.n = n

    # 实例化方法
    def foo(self, x, y):
        # 实例属性,不是只有写在init里面的才是实例属性,只不过该实例属性只能在该方法中使用,其他地方不能调用
        self.y = y
        print("executing foo(%s, %s)" %(self, x))  # self为实例化对象,x为传入参数的值
        print("self: ", self)  # 打印self是什么

        # 实例化方法里面可以调用类方法
        print(self.class_foo(self.m))  # 实例化对象b就是相当于self,只不过self在里面访问,b在外面访问
        print(self.class_foo(self.y))  # 运行self.class_foo(self.y)函数,相当于b.class_foo(self.y), b.class_foo(self.y)的返回值为None,所以打印None

        print(x)
        # print(self.class_foo(self.x))  # 如果前面不加self.x = x,那么x就仅仅只是一个入口参数,不是实例化属性

    # 类方法
    @classmethod
    def class_foo(cls, x):
        print("executing class_foo(%s, %s)" %(cls, x))
        print('cls:', cls)  # 打印cls是什么
        print(cls.d)  # cls为类,cls.d为类属性
        # print(cls.n)  #报错,因为n是实例属性,不是类属性

    # 静态方法
    @staticmethod
    def static_foo(x):
        print("executing static_foo(%s)" %x)

b = A(10, 2)  # __init__初始化需要传入两个参数
b.foo("a", "y")  # 实例化对象foo方法需要传入两个参数

输出结果:

executing foo(<__main__.A object at 0x000002C3EA64DAC0>, a)
self:  <__main__.A object at 0x000002C3EA64DAC0>
executing class_foo(<class '__main__.A'>, 10)
cls: <class '__main__.A'>
0
None
executing class_foo(<class '__main__.A'>, y)
cls: <class '__main__.A'>
0
None
a

类方法还有一个应用场景就是对类的初始化参数做预先处理,比如类在使用的时候一般初始化参数格式是固定的,而这个时候又需要传入其他格式的参数来初始化,这个时候就可以用到类方法。

看下面定义的一个时间类:

class Data_test(object):
    day=0
    month=0
    year=0
    def __init__(self,year=0,month=0,day=0):
        self.day=day
        self.month=month
        self.year=year

    def out_date(self):
        print "year :"
        print self.year
        print "month :"
        print self.month
        print "day :"
        print self.day

t=Data_test(2016,8,1)
t.out_date()

输出:

year :
2016
month :
8
day :
1

如果用户输入的是“2016-8-1"这样的字符格式,那么就需要调用Date_test类前做一下处理:

string_date='2016-8-1'
year,month,day=map(int,string_date.split('-'))
s=Data_test(year,month,day)

先把‘2016-8-1’ 分解成 year,month,day 三个变量,然后转成int,再调用Date_test(year,month,day)函数。 也很符合期望。

那我可不可以把这个字符串处理的函数放到 Date_test 类当中呢?那么@classmethod 就开始出场了

class Data_test2(object):
    day=0
    month=0
    year=0
    def __init__(self,year=0,month=0,day=0):
        self.day=day
        self.month=month
        self.year=year

    @classmethod
    def get_date(cls,
string_date):
        #这里第一个参数是cls, 表示调用当前的类名
        year,month,day=map(int,string_date.split('-'))
        date1=cls(year,month,day)
        #返回的是一个初始化后的类
        return date1

    def out_date(self):
        print "year :"
        print self.year
        print "month :"
        print self.month
        print "day :"
        print self.day

在Date_test类里面创建一个成员函数, 前面用了@classmethod装饰。 它的作用就是有点像静态类,比静态类不一样的就是它可以传进来一个当前类作为第一个参数。那如何调用呢?

r=Data_test2.get_date("2016-8-6")
r.out_date()

输出:

year :
2016
month :
8
day :
1

这样子等于先调用get_date()对字符串进行处理,然后才使用Data_test的构造函数初始化。

这样的好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以了。

6 方法重写

  • (1)避免用下划线作为变量名的开始,因为下划线对解释器有特殊的意义
  • (2)双下划线 变量名 双下划线:定义的是特殊方法,类似__init__()之类的
  • (3)单下划线 变量名:“单下划线”开始的成员变量叫作保护变量,意思是只有类对象和子类对象自己能访问到这些变量。以单下划线开头的代表不能直接访问的类属性,需要通过类提供的接口进行访问,不能用“from xxx import * ”而导入
  • (4)双下划线 变量名:“双下划线”开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据
class Parent:  # 定义父类    
    def myMethod(self):
        print('调用父类方法')

class Child(Parent):  # 定义子类
    def myMethod(self):
        print('调用子类方法')

c = Child()  # 子类实例
c.myMethod()  # 子类调用重写方法
super(Child, c).myMethod()  # 用子类对象调用父类已被覆盖的方法
调用子类方法
调用父类方法

7 获取对象信息

当我们拿到一个对象的引用时,如何知道这个对象是什么类型、有哪些方法呢?

7.1 使用type()

# 基本类型的判断
print(type(123))
print(type('str'))
print(type(None))

# 对函数或类类型的判断
class Animal(object):
    pass
a = Animal()
print(type(abs))
print(type(a))
<class 'int'>
<class 'str'>
<class 'NoneType'>
<class 'builtin_function_or_method'>
<class '__main__.Animal'>

type()函数返回的是Class类型。如果我们要用if语句中判断,就需要比较两个变量的type类型是否相同。

print(type(123) == type(456))
print(type(123) == int)
print(type('abc') == str)
print(type('abc') == type(123))
# 如何判断一个对象是否是函数?可以使用types模块中定义的常量:
import types
def fn():
    pass

print(type(fn) == types.FunctionType)
print(type(abs) == types.BuiltinFunctionType)
print(type(lambda x: x) == types.LambdaType)
print(type(x for x in range(10)) == types.GeneratorType)

输出结果:

True
True
True
False
True
True
True
True

7.2 使用instance()

对于class的继承关系来说,使用type()很不方便。因此我们要判断class类型,可以使用isinstance()函数。

class Animal(object):

    def run(self):
        print('Animal is running')

class Dog(Animal):

    def run(self):
        print('Dog is running')

a = Animal()
d = Dog()
print(isinstance(a, Animal))
print(isinstance(d, Dog))
print(isinstance(d, Animal))  # Dog是从Animal继承的,所以d对象也是dog
# 判断基本类型
print(isinstance('a', str))
print(isinstance(123, int))
print(isinstance(b'a', bytes))
# 判断一个变量是否是某些类型中的一种
print(isinstance([1, 2, 3], (list, tuple)))
print(isinstance((1, 2, 3), (list, tuple)))

7.3 使用dir()

使用dir()可以获得一个对象所有的属性和方法,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

dir('ABC')

8 相关定义

  • (1)类(class):用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例,类对象的类型。类是抽象的概念,而对象是一个你能够摸得着,看得到的实体。两者相辅相成,谁也离不开谁。
  • (2)类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • (3)数据成员:类变量或者实例变量,用于处理类及其实例对象的相关数据。
  • (4)方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫覆盖(overide),也称为方法的重写。
  • (5)局部变量:定义在方法中的变量,只作用于当前实例的类
  • (6)实例变量:在类的声明中,属性使用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
  • (7)继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
  • (8)实例化:创建一个类的实例,类的具体对象
  • (9)方法:类中定义的函数。
  • (10)对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

9 总结

类是具有相同属性和方法的对象的集合,因此,类的构成也包括属性和方法的两部分。创建类就是定义属性(类属性和实例属性)和方法(实例化方法、静态方法和类方法),然后对创建的类进行实例化并根据需要进行响应的操作。其中,对属性和方法的操作包括自定义、继承、多态、重写、调用等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值