5.面向对象

访问限制
继承和多态
获取对象信息
set、get、has
类属性和实例属性

一.面向对象基础

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

def printInfo(self):
    print(self.name, self.sex)


std1 = Student("Mary", '女')
std2 = Student('Tom', '男')

std1.printInfo()
std2.printInfo()
输出结果:
Mary 女
Tom 男

定义一个类的关键字和java一样也是用class,后面(object)代表该类继承自object类,object类是所有类的基类,在python里如果没有特定的类去继承就继承object类,init()方法有点类似于java的构造函数,参数中的self就相当于java里的this,代表该实例对象本身,不过不同的是java构造函数的参数里不需要写self这个参数,在Python里实际调用时也是不需要传这个self参数的,python解释器会自动把实例传进去,另一个不一样的地方是,java需要先在类里声明成员变量,再在构造函数里进行this.name = name等赋值,而python相当于是直接在init()里通过self.name = name做了成员变量声明和赋值了;第6行是对象里定义方法和普通方法的不同就是必须要写一个self参数,实际调用时是不用传的,第10行和11行是分别初始化两个Student实例,然后可以通过实例对象调用实例里的方法。

类就是实例的一个模板,实例是根据这个模板创建的具体对象,各个实例的数据是互相独立,互不影响的;

Python的类静态语言的类不同,Python可以随时对实例定义新的成员变量,虽然多个实例是同一个类型,但是里面的变量可能不同,例如:

std1.age = 18
print(std1.age)
print(std2.age)

结果:
Traceback (most recent call last):
  File "E:/python/project/test5/test5_1.py", line 18, in <module>
    print(std2.age)
AttributeError: 'Student' object has no attribute 'age'
18

二、访问限制
类里的变量或方法前如果加上__(两个下划线)就成了private的了外部是无法访问的:

class Student(object):
def __init__(self, name, sex):
    self.name = name
    self.sex = sex
    self.__age = 18

def printInfo(self):
    print(self.name, self.sex)



std1 = Student("Mary", '女')

std1.printInfo()

print(std1.age)
输出结果:
Mary 女
Traceback (most recent call last):
  File "E:/python/project/test5/test5_2.py", line 20, in <module>
    print(std1.get_age())
AttributeError: 'Student' object has no attribute 'get_age'

这时可以通过一个public的方法来访问私有资源:

class Student(object):
def __init__(self, name, sex):
    self.name = name
    self.sex = sex
    self.__age = 18

def printInfo(self):
    print(self.name, self.sex)

def get_age(self):
    return self.__age


std1 = Student("Mary", '女')

std1.printInfo()

# print(std1.age)

print(std1.get_age())
输出结果:
Mary 女
18

如果一个变量名以一个下划线_开头,在外部是可以访问到的,但是按照约定俗成的规定,这样的变量虽然在外部可以访问,但是要把它视为私有变量,不要随意访问。
实际上双下划线开头的变量在外部也可以通过一种方式访问到,例如__age属性,可通过如下方式访问:

print(std1._Student__age)

之所以直接访问不到,是因为__age变量被解释器把名字改为了_Student__age了;但是强烈建议不要这么做,不同python版本会把这种变量改为不同的名字,而且私有变量本来就不希望外部随便去操作,虽然Python没有用一种机制来阻止你这么做,但是还是要自觉维护这个规则。
另外还要注意一个问题,例如下面程序:

class Student(object):
def __init__(self, name, sex):
    self.name = name
    self.sex = sex
    self.__age = 18

def printInfo(self):
    print(self.name, self.sex)

def get_age(self):
    return self.__age


std1 = Student("Mary", '女')

std1.printInfo()

print(std1.get_age())

std1.__age = 20
print(std1.__age)
print(std1.get_age())
输出结果:
Mary 女
18
20
18

类实例里已经有一个__age变量了,第20行又设置了一下__age变量的值,这个__age和类实例里原来的那个__age其实不是同一个变量,此时类实例里原来的__age实际上已经变成了_Student__age,值依然是18,然后多了一个普通变量__age,值为20。

三、继承和多态

Python中的继承和多态与JAVA的有很大的相似性,几乎是一样的使用方式

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


class Cat(Animal):
    pass


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


def f1(animal):
    animal.run()


f1(Cat())
f1(Dog())
f1(Animal())
输出结果:
Animal is running
Dog is running
Animal is running

继承和多态的特性和好处就不记录了,只记录下Python和JAVA的不同点:
Java属于静态语言,如果一个方法接收的参数要求是Animal类型,就只能传Animal类型或其子类型,否则无法调用run方法;
但是对于Python这样的动态语言来说,就不一定非要传入Animal类型了,只需要保证传入的对象有一个run()方法就可以了,这样岂不是不需要继承就能把多态的其中一个特性给实现了,真乃奇葩。。哦不,真是神奇。

def f1(animal):
    animal.run()

class aaa(object):
    def run(self):
        print('aaa is running')


f1(aaa())
输出结果:
aaa is running

四、获取对象信息

import types
import test5.test5_3

print(type(123))
print(type('123'))
print(type(123) == int)
print(type('abc') == str)
print(type('abc') == type('1'))


# 判断是否是函数
def f1():
    pass


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

print(type(test5.test5_3.Cat()) == test5.test5_3.Animal)  # false

除了第21行是False,其它全是True。
在很多场景,需要判断一种类型是否属于其本身或是父类型的继承链上,这时可以用isinstance()方法来判断:

print(isinstance(test5.test5_3.Cat(), test5.test5_3.Animal)) #True  

isinstance还可以判断一个变量是否是多个类型中的一种:

print(isinstance((1, 2, 3), (int, str))) #False

# 获取'123'这个实例里所有的属性和方法
print(dir('123'))

输出结果:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

类似len这种属性和方法在Python中都是有特殊用途的,例如调用len()函数来获取一个对象的长度,在len()函数内部会去调用传入的对象的len()方法:

len('ABC')
'ABC'.__len__()

自己定义的类如果也想用len(obj),可以自己写一个len()方法:

class ddd(object):
def __len__(self):
    return 100

d = ddd()
print(len(d))
输出结果:
100

五、set、get、has
Python里也有类似Java里javabean的get、set方式

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


p = Person("Tom", 19)
print(hasattr(p, "name"))

setattr(p, "name", 'Mary')
print(getattr(p, 'name', "11"))  # 第三个参数是默认值,如果获取不到就返回这个默认值
输出结果:
True
Mary

如果获取一个没有定义的属性,会报错:

print(getattr(p, "sex"))
输出结果:
Traceback (most recent call last):
  File "E:/python/project/test5/test5_5.py", line 13, in <module>
    print(getattr(p, "sex"))
AttributeError: 'Person' object has no attribute 'sex'

如果给个默认值就不会报错了:

print(getattr(p, "sex", "男"))

输出结果:
男

还可以通过getattr获取一个实例里的方法:

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

def f1(self, msg):
    print('persion say ' + msg)


p = Person("Tom", 19)

f = getattr(p, 'f1')
f('sss')
输出结果:
persion say sss

六、类属性和实例属性

class Person(object):
name = "Person"

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


p = Person("Tom")

print(p.name)  # 实例的name属性
print(Person.name)  # 类的name属性
p.name = 'Mary'  # 修改实例的name属性
print(p.name)
print(Person.name)
del p.name  # 删除实例的name
print(p.name)

输出结果:
Tom
Person
Mary
Person
Person

类属性相当于这个类的一个成员变量,实例属性就相当于众多类实例其中的一个实例的一个局部变量,如果同时定义一个相同名字的实例属性和一个类属性,上面代码可见具体区别,所以不要把实例属性和类属性定义相同的名字,避免出现错误。

猜你喜欢

转载自blog.csdn.net/aislli/article/details/81163521
今日推荐