Python 笔记(19)— 类属性(私有属性、公有属性、实例属性、局部变量)、类方法(实例方法、静态方法)、类继承、多重继承

1. 类的创建

类是对某个对象的定义,它包含有关对象动作方式的信息,包括它的名称、方法、属性和事件。

类不存在于内存中,因此它本身并不是对象。当程序运行需要引用类的代码时,就会在内存中创建一个类的新实例,即对象。

虽然只有一个类,但能以这个类在内存中创建多个相同类型的对象。

class Person(object):
    
    #类的方法中必须要有一个self参数,但是方法被调用时,不用传递这个参数
    def get_name(self):      
        print "my name is: lili"

    def get_age(self):
        print "my age is : 20"

    def get_hoppy(self):
        print "My hoppy is :lvyou"


boy = Person()
boy.get_name()       #但是方法被调用时,不用传递这个参数
boy.get_age()        #在调用get_age等方法时,boy自动将自己作为一个参数传入方法中,因此我们称它为self
boy.get_hoppy()

使用 PyCharm 在类中定义方法时,若该方法不涉及对属性的操作,那么会提示 Method xxx may be 'static',因为 PyCharm 会认为该方法是一个静态方法,而不是类方法,所提提示我们在该方法前添加 @staticmethod 装饰器进行装饰。

2. 类的封装和多态

所谓封装,即将抽象得到的数据和行为(功能)相结合,形成一个有机整体,也就是将数据和操作数据的源代码进行有机整合,形成类。

其中数据和函数都是类的成员。目的是隐藏对象的属性和实现细节,对外公开接口,在程序中控制属性的读和修改的访问级别。

封装和多态的区别:多态可以让用户对不知道是什么类的对象进行方法调用,而封装则不用关心对象是如何创建的而直接进行使用。

class MyClass(object):

    # 定义一个全局变量
    name = 'xml'

    def set_name(self, name):
        self.name = name

    def get_name(self):
        return self.name

my = MyClass()
my.set_name('Jack')
print my.get_name()

# 如果将变量存储到全局变量 name 中时,实例化 MyClass 之后,全局变量 name 将会改变
my = MyClass()
my.name = 'Tom'
print my.get_name()

# 对象有自己的状态,对象的状态由它的特性名称来描述,eg:下面 she 对象的内容不影响 my 对象的内容
she = MyClass()
she.set_name('angel')
print she.get_name()
print my.get_name()

3. 类的属性

  • 私有属性

    函数、方法或者属性的名称以两个下划线 _ 开始,则为私有类型;

以单下划线开头(_foo )的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用 from xxx import 导入;

以双下划线开头的(__foo )代表类的私有成员;

以双下划线开头和结尾的(__ foo__)代表Python里特殊方法专用的标识,如__init()__ 代表类的构造函数;

  1. __attribute:属性名前面加两个下划线,即声明该属性为私有,不能在类的外部直接访问,在类内部访问时用 self.__attribute
  2. __function:函数名前面加两个下划线,即声明该函数为私有,不能在类的外部直接访问,在类内部访问时用 self.__function
  • 公有属性

    如果函数、方法或者属性的名称没有以两个下划线开始,则为公有属性;

  • 实例属性

    self 作为前缀的属性;

  • 局部变量

    类的方法中定义的变量没有使用 self 作为前缀声明,则该变量为局部变量;

class Fly():
    price = 123                      # 类的公有属性

    def __init__(self):
        self.direction = "beijing"   # 类的公有实例属性 
        self.__city = '陕西'          #	私有的实例属性
        isPeople = "more people"     # 局部变量


if __name__ == '__main__':
    print Fly.price
    #print Fly.direction   公有的实例属性在未实例化之前不能使用,该语句是错误的
    traveller = Fly()
    print traveller.direction
    #print traveller.__city    Fly instance has no attribute '__city'
    print traveller._Fly__city    #私有属性的访问方式:实例化对象名._类名__私有属性名
    #print traveller.isPeople  局部变量不能被实例化对象traveller所引用
    Fly.price = traveller.price + 10
    print "the Fly prise is " + str(Fly.price)
    mytraveller = Fly()
    print "I think the price is " + str(mytraveller.price)

数据属性不需要预先定义,数据属性初次被使用时,即被创建并赋值。

class Data_attribute():
    pass

if __name__ == "__main__":
    data = Data_attribute()
    data.name = "我是没有被预先定义的数据"
    print data.name

3.1 类数据属性

  • 类数据属性属于类本身,可以通过类名进行访问、修改
  • 类数据属性也可以被类的所有实例访问、修改
  • 在类定义之后,可以通过类名动态添加类数据属性,新增的类属性也被类和所有实例共有
class Student(object):
    count = 0
    books = []
'''
"count" "books" "name"和"age"都被称为类的数据属性,但是它们又分为类数据属性和实例数据属性
'''
    def __init__(self, name, age):      # 类的初始化函数"__init__"
        self.name = name
        self.age = age
    pass

# 1、类数据属性

Student.books.extend(['python', 'javascript'])
print 'Student book list: %s' % Student.books
# Student book list: ['python', 'javascript']
# class can add class attribute after class define

Student.hobbies = ['reading', 'jogging', 'swimming']
print 'Student hobbies list: %s' % Student.hobbies
# Student hobbies list: ['reading', 'jogging', 'swimming']
print dir(Student)
'''
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'books', 'count', 'hobbies']
'''

3.2 实例数据属性

  • 实例数据属性只能通过实例访问
  • 在实例生成后,还可以动态添加实例数据属性,但是这些实例数据属性只属于该实例
class Student(object):
    count = 0
    books = []
'''
"count" "books" "name"和"age"都被称为类的数据属性,但是它们又分为类数据属性和实例数据属性
'''
    def __init__(self, name, age):      # 类的初始化函数"__init__"
        self.name = name
        self.age = age
    pass
    
# 2、实例数据属性

wilber = Student('wilber', 28)
print '%s is %d years old' % (wilber.name, wilber.age)
# wilber is 28 years old
# class instance can add new attribute
# "gender" is the instance attribute only belongs to wilber

wilber.gender = 'male'
print '%s is %s' % (wilber.name, wilber.gender)

# class instance can access class attribute

print dir(wilber)
wilber.books.append('C#')
print wilber.books


will = Student("Will", 27)
print "%s is %d years old" % (will.name, will.age)
# will shares the same class attribute with wilber
# will don't have the "gender" attribute that belongs to wilber
print dir(will)
print will.books

4. 类的方法

  • 类方法

    类方法使用 @classmethod 指令来声明;

  • 类实例方法

    实例方法则无需使用指令来说明;实例方法的第一个参数必须是 self实例方法只能通过类实例进行调用,这时候 self 就代表这个类实例本身。通过 self 可以直接访问实例的属性;

  • 类静态方法
    与实例方法和类方法不同,静态方法没有参数限制,既不需要实例参数,也不需要类参数,定义的时候使用 @staticmethod 装饰器,同类方法一样,静态法可以通过类名访问,也可以通过实例访问。

这三种方法的主要区别在于参数,

  • 实例方法被绑定到一个实例,只能通过实例进行调用;
  • 但是对于静态方法和类方法,可以通过类名和实例两种方式进行调用;
class MyClass():

    def __init__(self):
        pass

    def aa(self, message):    # 类实例方法
        print "执行实例方法 aa(%s,%s)" %(self,message)

    @classmethod
    def class_method(cls, message):  # 类方法
        print "执行类方法 class_method(%s,%s)" %(cls,message)

    @staticmethod
    def static_method():    # 类静态方法
        print "我是被定义的静态方法"

if __name__ == "__main__":
    qq = MyClass()
    qq.aa('hello')
    # My_object.aa('hello')  类实例方法aa只能被类实例调用,不能被类调用

    # 类方法和静态方法可以直接被类和类实例调用
    qq.class_method('hello')
    MyClass.class_method('hello')
    qq.static_method()
    MyClass.static_method()

为什么要用到静态方法呢? 如下示例说明:

静态方法用到的场景就是和类相关的操作,但是又不会依赖和改变类、实例的状态,比如经常有跟类有关系的函数,我们希望它在运行时又不需要实例和类参与的情况下直接就能调用。调用静态方法可以无需创建对象。

class Robot(object):
    def __init__(self, data):
        self.data = data

    @staticmethod
    def is_need():
        return True

    def reboot(self):
        if Robot.is_need():	# 调用类中的静态方法
            print "The pc will reboot for {0}".format(self.data)

    def restore(self):
        if Robot.is_need():
            print "The pc will restort for {0}".format(self.data)

robot1 = Robot("cmd")
robot1.reboot()
robot1.reboot()

类方法的示例

In [15]: class Student(object):
    ...: 	num_student=0
    ...: 	def __init__(self):
    ...: 		Student.num_student+=1
    ...: 
    ...: 	@classmethod
    ...: 	def get_num_of_instance(cls):
    ...: 		return cls.num_student
    ...: 

In [16]: s1=Student()

In [17]: s1.get_num_of_instance()
Out[17]: 1

In [18]: s2=Student()

In [19]: s2.get_num_of_instance()
Out[19]: 2

In [20]: Student.get_num_of_instance()
Out[20]: 2

In [21]: s3=Student()

In [22]: Student.get_num_of_instance()
Out[22]: 3

Python 中,静态方法和类方法都是可以通过类对象和类对象实例访问classmethodstaticmethod 的区别是:

  • classmethod 是类方法,而 staticmethod 是静态方法;
  • @classmethod 是一个函数修饰符,它表示接下来的是一个类方法,类方法的第一个参数 cls
  • 而实例方法的第一个参数是 self,表示该类的一个实例;

普通对象方法至少需要一个 self 参数,代表类对象实例,类方法有类变量 cls 传入,从而可以用 cls 做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量 cls 是子类,而非父类。

对于类方法,可以通过类来调用,比如说 A 是一个类,那么我们可以通过 A.method() 来调用 A 里面的 method 方法, 也可以通过类的一个实例来调用,如 A().method() 进行调用,首先 A() 方法会调用 A 的初始化方法进行实例化出一个 A 的对象,然后通过该对象调用 method 方法。

静态方法则没有上述方法,它基本上跟一个全局函数相同,一般来说用的很少。

5. 类的继承

class 派生类名(基类名):
    类体

Python 中继承中的一些特点:

  • 在继承中基类的构造(__init__() 方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
  • 在调用基类的方法时,需要使用 super() 前缀。
  • Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找(先在本类中查找调用的方法,找不到才去基类中找)。

继承是指一个对象直接使用另一个对象的属性和方法,当一个类拥有另一个类的所有数据和操作时,就称这两个类之间具有继承关系,被继承的类称为父类或者超类,继承了父类或者超类的所有数据和操作的类称为子类。

继承类语法:

class class_name(fatherclass_name) 

fatherclass_name 代表的是 class_name 要继承的类 。

如何调用父类的方法:使用类名访问父类中的方法,并在参数列表中引入对象 self

class Father():
    def __init__(self):
        print '我是初始化Father类中的方法'
        print '供以后调用'


class Son(Father):
    def __init__(self):
        print '我是初始化Son类中的方法'
        Father.__init__(self)   # 使用类名访问父类中的方法,并在参数列表中引入对象self

child = Son()

下面看一个例子,我们以学校的老师和学生为例,老师和学生有共同的特征:姓名、年龄、地址、喜好,不同的是老师有工资,学生有成绩。

我们将学校成员归为一个共同的类。

class SchoolMember(object):
    def __init__(self, name, age, addr, hoppy):
        self.name = name
        self.age = age
        self.addr = addr
        self.hoppy = hoppy
        print '初始化的名字是%s' %self.name

    def tell(self):
        print '姓名:%s,年龄:%s,地址:%s,喜好:%s'  %(self.name,self.age,self.addr,self.hoppy)

Teacher 类继承 SchoolMember 类,采用新式类继承写法时,super() 会带两个参数,第一个是子类的类名,第二个是 self 参数。

super() 可以避免一些类继承的潜在问题,特别是在多重继承上。

class Teacher(SchoolMember):
    def __init__(self, name, age, addr, hoppy, salary):
        # 使用类名访问父类中的方法,并在参数列表中引入对象self
        SchoolMember.__init__(self, name, age, addr, hoppy)  
        # 新式类继承写法
        # super(Teacher, self).__init__(name, age, addr, hoppy)
        # 或者
        # super().__init__(name, age, addr, hoppy)
        self.salary = salary    # 添加salary属性
        print '我是继承SchoolMember的老师%s' %(self.name)

    def tell(self):
        SchoolMember.tell(self)
        # 或者
        # super.tell()
        print '我这次的工资是 %s' %self.salary

Student 类继承 SchoolMember

class Student(SchoolMember):
    def __init__(self, name, age, addr, hoppy, marks):
        SchoolMember.__init__(self, name, age, addr, hoppy)
        # 新式类继承写法
        # super(Student, self).__init__(name, age, addr, hoppy)
        # 或者
        # super().__init__(name, age, addr, hoppy)
        self.marks = marks      # 添加marks属性
        print '我是继承SchoolMember的学生%s' %(self.name)

    def tell(self):
        SchoolMember.tell(self)
        # 或者
        # super.tell()
        print '我这次的成绩是 %s' %self.marks
        
# 分别创建Teacher和Student的实例
t = Teacher('Tom', 40, '陕西西安', '篮球',3000)
s = Student('jack', 14, '北京', '足球', 90)

members = (t, s)
for member in members:
    member.tell()
    
'''
程序输出结果如下:
我是初始化Son类中的方法
我是初始化Father类中的方法
供以后调用
初始化的名字是Tom
我是继承SchoolMember的老师Tom
初始化的名字是jick
我是继承SchoolMember的学生jick
姓名:Tom,年龄:40,地址:陕西西安,喜好:篮球
我这次的工资是 3000
姓名:jack,年龄:14,地址:北京,喜好:足球
我这次的成绩是 90
'''

6. 类的多重继承

子类继承多个父类的方法:

class class_name(fatherclass_name,fatherclass_name1,......)

其中,class_name为类名,fatherclass_namefatherclass_name1 为父类名。

class MyFather():
    def __init__(self):
        self.eye = '爸爸的眼睛是双眼皮'
        print self.eye


class MyMother():
    def __init__(self):
        self.forehead = '妈妈的额头有点宽'
        print self.forehead


class MyAunt():
    def __init__(self):
        self.nose = '姑姑的鼻子是高鼻梁'
        print self.nose


class MySelf(MyFather, MyMother, MyAunt):
    def __init__(self, face):
        print '我的眼睛是双眼皮,别人说我是继承的是:'
        MyFather.__init__(self)
        print '我的额头有点宽,别人说我是继承的是:'
        MyMother.__init__(self)
        print '我的鼻子有点宽,别人说我是继承的是:'
        MyAunt.__init__(self)
        self.face = face
        print '我的脸型:%s' %self.face,'这下终于没人说我像谁了'

me = MySelf('偏圆吧')

7. 通过对象修改类属性

通过对象可以修改类属性值。但这是危险的。类属性被所有同一类及其子类的对象共享。类属性值的改变会影响所有的对象。

class Human(object):
    Age = 0
    Name = ["Li", "Lei"]
 
a = Human()
b = Human()
 
a.Age += 1
print a.Age
print b.Age          # b.Age不会改变    Age是immutable型
 
a.Name[0] = "Wang"
print a.Name
print b.Name        # b.Name会改变     Name是mutable型

'''
程序输出结果如下:
1
0
['Wang', 'Lei']
['Wang', 'Lei']
'''

为什么 immutable 变量是可行的呢?

原因是,在更改对象属性时,如果属性是 immutable 的,该属性会被复制出一个副本,存放在对象的__dict__ 中。你可以通过下面的方式查看:

print a.__class__.__dict__
print a.__dict__

注意到类中和对象中各有一个 Age。一个为 0, 一个为 1。所以我们在查找 a.Age 的时候,会先查到对象的 __dict__ 的值,也就是 1。

mutable 的类属性,在更改属性值时,并不会有新的副本。所以更改会被所有的对象看到。

8. 类中的方法调用其它方法

class Human(object):
    laugh = 'hahahahaha'

    def show_laugh(self):
        print self.laugh

    def laugh_10th(self):
        for i in range(10):
            self.show_laugh()

li_lei = Human()
li_lei.laugh_100th()
#类属性laugh ,在方法show_laugh()中,通过self.laugh,调用了该属性的值。
#还可以用相同的方式调用其它方法。方法show_laugh(),在方法laugh_100th中()被调用
  • 14
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值