python面向对象

1. 实例属性

在定义 Person 类时,可以为Person类添加一个特殊的__init__()方法,当创建实例时,init()方法被自动调用,我们就能在此为每个实例都统一加上以下属性。

class Person(object):
    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

init() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),后续参数则可以自由指定,和定义函数没有任何区别。

xiaoming = Person('Xiao Ming', 'boy', 13)
xiaohong = Person('Xiao Hong', 'girl', 14)

print(xiaohong.name)
print(xiaohong.sex)
print(xiaohong.age)
# 但当访问不存在的属性时,会报错
print(xiaohong.birth)

2. 类属性

类和实例对象是有区别的,类是抽象,是模板,而实例则是根据类创建的对象,比如类:动物,只是一个抽象,并没有动物的详细信息,而猫、狗等,则是具体的动物,是类的对象。

在前面,实例对象绑定的属性只属于这个实例,绑定在一个实例上的属性不会影响其它实例;同样的,类也可以绑定属性,但是类的属性不属于任何一个对象,而是属于这个类。如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。

class Animal(object):
	'''
	localtion就是属于Animal这个类的类属性,此后,通过Animal()实例化的所有对象,
	都可以访问到localtion,并且得到唯一的结果。
	'''
    localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age
     
dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
print(dog.localtion) # ==> Asia
print(cat.localtion) # ==> Asia
# 类属性,也可以通过类名直接访问
print(Animal.localtion) # ==> Asia

'''
类属性也是可以动态添加和修改的,需要注意的是,因为类属性只有一份,
所以改变了,所有实例可以访问到的类属性都会变更:
'''
Animal.localtion = 'Africa'
print(cat.localtion) # ==>Africa
print(dog.localtion) # ==>Africa

# demo
class Animal():
    count = 0
    def __init__(self,name,age):
        self.name = name
        self.age = age
        Animal.count += 1
        
dog = Animal("wangwang",1)
print(dog.count)
cat = Animal("mimi",2)
print(cat.count)
pig = Animal("panpan",3)
print(pig.count)

3. 类属性和实例属性的优先级

属性可以分为类属性和实例属性,那么问题就来了,如果类属性和实例属性名字相同时,会怎么样?

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.localtion = localtion

dog = Animal('wangwang', 1, 'GuangDong')
cat = Animal('mimi', 3, 'ChongQing')
print(dog.localtion) # ==> GuangDong
print(cat.localtion) # ==> ChongQing
print(Animal.localtion) # ==> Asia 

可见,在类属性和实例属性同时存在的情况下,实例属性的优先级是要高于类属性的,在操作实例的时候,优先是操作实例的属性。另外,当实例没有和类同名的时候,通过实例对象,依然可以访问到类属性。

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age

cat = Animal('mimi', 3)
print(cat.localtion) # ==> Asia

# 但是不能将其修改,下面的操作​只是新增了实例属性
cat.localtion = 'Africa'
print(Animal.localtion) # ==> Asia
print(cat.localtion) # ==> Africa

4. 私有属性

并不是所有的属性都可以被外部访问的,不能被外部访问的属性称为私有属性。私有属性是以双下划线’__'开头。
在外部访问私有属性将会抛出异常,提示没有这个属性。虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。私有属性是为了保护类或实例属性不被外部污染而设计的。

# 类私有属性
class Animal(object):
    __localtion = 'Asia'

# 直接访问类私有属性报错
print(Animal.__localtion)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Animal' has no attribute '__localtion'
# 实例私有属性
class Animal(object):
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.__localtion = localtion

dog = Animal('wangwang', 1, 'GuangDong')
print(dog.name) # ==> wangwang
print(dog.age) # ==> 1

# 直接访问实例私有属性报错
print(dog.__localtion)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute '__localtion'

5. 实例方法

上面提到,私有属性没有办法从外部访问,只能在类的内部操作;那如果外部需要操作私有属性怎么办?这个时候可以通过定义类或者实例的方法来操作私有属性,实例的方法指的就是在类中定义的函数,实例方法的第一个参数永远都是self,self是一个引用,指向调用该方法的实例对象本身,除此以外,其他参数和普通函数是完全一样的。,而下面的get_name(self) 就是一个实例方法,在实例方法里面是可以操作私有属性的。另外,init(self, name)其实也可看做是一个特殊的实例方法。通过定义get_name(self)方法,在外部就可以通过这个方法访问私有属性了。

class Person(object):

    def __init__(self, name):
        self.__name = name
        
    def get_name(self):
        return self.__name

​p = Person('Alice')
print(p.get_name()) # ==> Alice

在外部调用实例方法时,是不需要显式传递self参数的。另外,通过定义实例方法来操作私有属性的这种方法是推荐的,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。当然,实例方法并不仅仅是为私有属性服务的,我们可以把和类的实例有关的操作都抽象成实例方法,比如:打印实例的详细信息等等。

class Animal(object):
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.localtion = localtion

    def get_info(self):
        return 'name = {}, age = {}, localtion = {}'.format(self.name, self.age, self.localtion)

dog = Animal('wangwang', 1, 'GuangDong')
print(dog.get_info())

6. 类方法

在上面,为了操作实例对象的私有属性,我们定义了实例方法;同样的,如果需要需要操作类的私有属性,则应该定义类的方法。默认的,在Class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。要在class中定义类方法,需要这么写:

# demo1
class Animal(object):
    __localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def set_localtion(cls, localtion):
        cls.__localtion = localtion

    @classmethod
    def get_localtion(cls):
        return cls.__localtion

print(Animal.get_localtion()) # ==> Asia
Animal.set_localtion('Afica')
print(Animal.get_localtion()) # ==> Africa
# demo2
class Animal(object):
    __localtion = 'Asia'
    __count = 0
    def __init__(self, name, age):
        self.name = name
        self.age = age
        Animal.__count += 1

    @classmethod
    def get_count(cls):
        return cls.__count

dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
pig = Animal('panpan', 1)
count = Animal.get_count()
print(count)

和实例方法不同的是,这里有几点需要特别注意:

  • 类方法需要使用@classmethod来标记为类方法,否则定义的还是实例方法
  • 类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.__localtion 实际上相当于Animal.__localtion。
  • 因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用

7. 类继承

7.1 单继承

对人类的抽象可以定义为Person类,而学生、老师等,也都是人类,所以如果定义学生Student的类,可以继承Person类

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

student = Student('Alice', 'girl', 100)
print(student.name) # ==> Alice
print(student.gender) # ==> girl
print(student.score) # ==> 100

在定义继承类的时候,有几点是需要注意的:

  • class Student()定义的时候,需要在括号内写明继承的类Person
  • 在__init__()方法,需要调用super(Student, self).init(name, gender),来初始化从父类继承过来的属性

7.2 多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。多重继承和单继承没有特别大的差异,只是在括号内加入多个需要继承的类的名字即可。

class A(object):
    def __init__(self, a):
        print ('init A...')
        self.a = a

class B(A):
    def __init__(self, a):
        super(B, self).__init__(a)
        print ('init B...')

class C(A):
    def __init__(self, a):
        super(C, self).__init__(a)
        print ('init C...')

class D(B, C):
    def __init__(self, a):
        super(D, self).__init__(a)
        print ('init D...')

在这里插入图片描述
实践证明,在多重继承里,A虽然被继承了两次,但是__init__()的方法只调用一次。多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。

>>> d = D('d')
init A...
init C...
init B...

8. 判断类型

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

isinstance 判断类型

>>> isinstance(p, Person)
True # p是Person类型
>>> isinstance(p, Student)
False # p不是Student类型
>>> isinstance(p, Teacher)
False # p不是Teacher类型

这说明在继承链上,一个父类的实例不能是子类类型,因为子类比父类多了一些属性和方法,再看 s:

>>> isinstance(s, Person)
True # s是Person类型
>>> isinstance(s, Student)
True # s是Student类型
>>> isinstance(s, Teacher)
False # s不是Teacher类型

s 是Student类型,不是Teacher类型,这很容易理解。但是s 也是Person类型,因为Student继承自Person,虽然它比Person多了一些属性和方法,但是,把 s 看成Person的实例也是可以的。这说明在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型。

顺便提一句,isinstance也可以用于Python自有数据类型的判断。

​s = 'this is a string.'
n = 10
isinstance(s, int) # ==> False
isinstance(n, str) # ==> False

9. 多态

类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个who() 方法:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is %s' % self.name

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
    def who(self):
        return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course
    def who(self):
        return 'I am a Teacher, my name is %s' % self.name

p = Person('Tim', 'Male') # ==> I am a Person, my name is Tim
s = Student('Bob', 'Male', 88) # ==> I am a Student, my name is Bob
t = Teacher('Alice', 'Female', 'English') # ⇒ I am a Teacher, my name is Alice

从定义上来讲,Student和Teacher都拥有来自父类Person继承的who()方法,以及自己定义的who()方法。但是在实际调用的时候,会首先查找自身的定义,如果自身有定义,则优先使用自己定义的函数;如果没有定义,则顺着继承链向上找。

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is %s' % self.name

class Boss(Person):
    def __init__(self, name, gender,company):
        super(Boss, self).__init__(name, gender)
        self.company = company

b = Boss('Bob', 'Male', 'Alibaba')
# 在Boss的定义类,没有定义who方法,所以会顺着继承链向上找到父类的who方法并且调用
b.who() # ==> I am a Person, my name is Bob

10. 获取对象信息

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

p = Person('Alice', 'Female')
s = Student('Bob', 'Male', 100)
type(p) # ==> <class '__main__.Person'>
type(s) # ==> <class '__main__.Student'>
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is {}'.format(self.name)

p = Person('Alice', 'Female')
dir(p)

['__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__', 'gender', 'name', 'who']

dir()返回所有实例属性,包括__class__这类有特殊意义的属性。注意到方法who也是p的一个属性。dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() 和 setattr( )函数了。

>>> getattr(p, 'name') # 获取name属性
'Alice'
>>> setattr(p, 'name', 'Adam') # 设置新的name属性
>>> s.name
'Adam'
>>> getattr(s, 'age') # 获取age属性,但是属性不存在,报错:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'
>>> getattr(s, 'age', 20) # 获取age属性,如果属性不存在,就返回默认值20
class Person(object):
    def __init__(self, name, gender, **kw):
        self.name = name
        self.gender = gender
        for k, v in kw.items():
            setattr(self, k, v)

p = Person('Bob', 'Male', age=18, course='python')
print(p.age) # ==> 18
print(p.course) # ==> python
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值