目录html
想学爬虫仍是 python 专业啊,以前一直在用 java, 如今决定尝尝鲜,使用 python及爬虫框架来完成网络数据采集。
编程语言之间都是相通的,好比都须要模块化,引入其余文件来实现功能,使用列表等容器来处理数据,都要使用 json
或 xml
来解析和传输数据。
你会发现 经过类比的方式,带着问题去学习,会走的很快
并且我认为 代码示例的做用是异常强大的, 我会尽可能使用代码示例的方式来展现,以知足同窗快速学习的须要,也备后续查询。java
在上篇文章 中,讨论了 python 3 中 string, 数据结构(Dict, List, 元组)等重要的主题。
今天我会继续探险,去征服 python 3 中的面向对象, let's go 让咱们出发吧!python
刚接触 python 中的类和对象,我也和大多数小伙伴同样迷茫,不知道它和我所熟知的 java 都有什么异同点,为此我还提出了一大堆问题编程
class
的继承?下面就一一来探索这些疑惑。json
在 java 中要建立一个类就必需要使用 class
关键字,要将类实例化,建立一个对象,可使用 new
关键字。在 python 中是怎么样的呢?网络
先看代码数据结构
class Person(): """这个叫作定义体,用来解释类的用途""" print(Person) # <class '__main__.Person'> # 因为是在程序顶层定义的,它的全名就是 '__main__.Person' person = Person() print(person) # <__main__.Person object at 0x000000000219A1D0>
要定义一个类(class) 只要将 class
关键字放在前面便可,类内部也能够像 java 似的定义变量和函数,这个后面再看。框架
实例化一个类,也就是建立一个对象,并不须要使用 new
关键字,只需将 class
当作函数来调用就能够啦,是否是比 java 简洁很多。编程语言
了解了定义和实例化类,还有两个问题:模块化
isinstance
print(isinstance (person, Person)) # True
type
print(type(person)) # <class '__main__.Person'>
上面的代码,光有一个空对象是干不了任何事情的,咱们也要像 java 同样为其定义属性和方法。
java 是不能动态定义一个变量的,必需要把它放在 class
中预先定义好才能够用;而在 python 中这却不是问题,不信看代码~
class Person(): """这个叫作定义体,用来解释类的用途""" person = Person() person.age = 5 print(person.age)
虽然在对 Person class
定义时没有任何属性的声明,但在实例化后依然能够添加 age 属性,并且也并无看到如 java 中 public
, private
等访问修饰符的存在, python 中有没有这些概念呢?还真有,变量默认就是 public
公有的,若是 在变量名前添加两个下划线,这样就会认为是 private
私有变量了,直接访问是不能够的。看下面代码
class Person(): """这个叫作定义体,用来解释类的用途""" gender = 'male' __age = 5 person = Person() print(person.gender) # male print(person.__age) # AttributeError: 'Person' object has no attribute '__age'
上面代码中,在打印 __age
时会报错,告知没有找到这个属性,其实就是 因为使用双下划线作前缀使其变成私有变量了。
那 函数名是否是也有私有函数,是否是也在前面加双下划线呢 ?猜的没错,这个咱们后面再了解。
既然 python 对象的属性操做如此灵活,能够动态添加,那用户在使用时就可能会碰到一些异常。
比较典型的就是,访问一个不存在的属性,会抛出 AttributeError
。对这种状况有两种方式能够处理:
hasattr
断定对象是否拥有该属性(记住,只对公有变量有效哦~)try
语句处理class Person(): """这个叫作定义体,用来解释类的用途""" gender = 'male' __age = 5 person = Person() print(hasattr(person, 'gender')) # True print(hasattr(person, 'name')) # False print(hasattr(person, '__age')) # False try: name = person.name except AttributeError: name = 'unknown' print(name)
什么是方法?方法和函数有什么区别?在上一篇我就介绍了好多 string
的方法,为何叫作方法,而不叫作 string
的函数呢?一块儿来了解下~
调用方法分为两种形式,分别是
先看第一种函数调用语法,这其实和 java 中的静态方法是同样的,只是前面不须要 static
关键字。
class Person: def print_person(person): print('name: %s, gender%s, age:%d' % (person.name, person.gender, person.age)) person = Person() person.name = 'Tom' person.gender = 'male' person.age = 10 Person.print_person(person)
函数调用语法的方式其实和单纯的函数调用,区别是不大的,由于方法前面的 class
对它没起什么做用,活动主体 依然是方法。
再看另一种 方法调用语法,而此次的主体则是调用该方法的 对象
class Person: __name = 'Tom' __gender = 'male' __age = 10 def print_person(self): print('name: %s, gender:%s, age:%d' % (self.__name, self.__gender, self.__age)) person = Person() person.print_person()
细心的同窗会发现这里在定义方法时形参为 self
, 而在调用方法时却没有任何入参。
那这个 self
是什么呢?
若是类比 java 的话,这个 self
能够看做是 this
, 其实就是对当前对象的引用。 java 中定义方法时没必要将其作入参。而这个 self
在 python 中则是必须声明的,在调用的时候则没必要传入。
注意,这个 self
可不是关键字哦,只要占据方法形参的头把交椅,你能够用任何名字。
在 java 中构造函数是与类同名的,并且会伴随着实例化的动做而执行。在 python 中呢?
python 中的构造函数叫作 init
方法,全名是 __init__
具体看下面代码
class Person(): __gender = 'male' __age = '0' def __init__(self, gender='male', age=0): self.__gender = gender self.__age = age person1 = Person('female', 10) person2 = Person() person2 = Person('male')
做为实例方法, self
入参固然少不了,其余参数就按照顺序排开,若参数不够,就用默认值来代替。
在java 中, 咱们通常会覆盖 toString() 方法来返回对象中包含的值得关注的信息。 python 中也有这样一个方法,叫作 __str__
。
class Person: __name = 'Tom' __gender = 'male' __age = 10 def __str__(self): return ('name: %s, gender:%s, age:%d' % (self.__name, self.__gender, self.__age)) person = Person() print(person)
做为最佳实践的一部分,建议你在每一个建立的类中都覆盖这个方法。
还记得面向对象的几个特征吗?封装性,继承性,多态性。嗯,来聊下 python 对多态的实现。
什么叫作多态?
在 java 中,若是在一个 class 中有多个函数,函数名相同而参数不一样(个数或类型不一样),就叫作多态。
而在 python 中, 多态的概念则更进一步,对于同一个函数,若是可以处理多种类型的数据,也叫作多态。
tuple_list = [(1, 2,), (2, 3,), (4, 5)] list = [1, 2, 3, 4] dict1 = { 'a' : 1, 'b' : 2 } def printSomething(something): for i in something: print(i) print(tuple_list) print(dict1) print(list)
printSomething
一个函数能够同时打印元组,列表以及字典,充分发挥代码复用的功效,是否是很方便。
聊完了多态,再来看看面向对象的另外一个特征:继承性。
什么是继承?继承就是定义好了一个类 A(父类);再定义一个新类 B(子类),类 B 拥有类 A 的方法和属性,而且又定义了新的属性和方法。类 A 称为父类,类 B 称为子类。
java 中定义两个类的继承关系,使用 extends
关键字实现,在 python 中呢?
class Father: """ 这是一个父类 """ __age = 45 class Son(Father): """ 这是一个子类 """
python 中不须要加关键字来讲明继承关系,只须要将父类的名称放在括号中就能够了,看起来要比 java
简洁一些。
前面讲过, python class 中能够定义本身的初始化函数,在实例化的时会被调用。那若是父类和子类都有初始化函数或者父类有而子类没有,那初始化函数该如何执行呢?这里分为三种状况来讲明,先来看第一种。
父类有 init 而子类没有, 这时父类的初始化函数会被默认调用
class Father(): """ 这是一个父类 """ def __init__(self, age): print("Father's init function invoke") self.__age = age class Son(Father): """ 这是一个子类 """ son = Son(5)
这里要注意,父类中须要的 age
参数必定要传进去哦,要否则会报错的。
父类,子类都有 init ,而子类没有显式调用父类的 init 方法时,父类初始化函数是不会被调用的
class Father(): """ 这是一个父类 """ def __init__(self, age): print("Father's init function invoke") self.__age = age def get_age(self): return self.__age class Son(Father): """ 这是一个子类 """ def __init__(self, age): print("Son's init function invoke") self.__age = age son = Son(5) # Son's init function invoke print(son.get_age()) # AttributeError: 'Son' object has no attribute '_Father__age'
细心的同窗会发现,代码中的最后一句报错了,表示 Son 对象没有 Father
类的 __age
变量。这是由于
__age
变量则没有初始化get_age
函数是被子类从父类继承来的,返回的是父类的 __age
变量那我要是想解决这个错误,该怎么作呢?有两种方法
Son
的初始化函数中显式调用父类 Father
的初始化函数Son
中从新定义个 get_age
方法,这样就会覆盖父类的同名方法,返回的是子类的 _age
变量第二种方法就不贴代码了,感兴趣的话能够试试。重点来看第一种方法,这就引出了第 3 种状况。
子类在本身定义的 init 方法中,显式调用父类的 init 方法,父类和子类的属性都会被初始化
class Father(): """ 这是一个父类 """ def __init__(self, age): print("Father's init function invoke") self.__age = age def get_age(self): return self.__age class Son(Father): """ 这是一个子类 """ def __init__(self, age): print("Son's init function invoke") self.__age = age super(Son, self).__init__(age + 25) def get_age(self): return self.__age def get_father_age(self): return super(Son, self).get_age() son = Son(5) # Son's init function invoke # Father's init function invoke print(son.get_father_age()) # 30 print(son.get_age()) # 5
看到代码中是怎么调用父类的初始化函数吗? 对,用的是 super
。
java 中也有 super
关键字,表示对父类的指代, python 的 super
是怎么用的,原理是什么?咱们来看下。
下面说明的只针对 python 单继承的状况,多继承这里暂不涉及,有兴趣的同窗能够自行充电。
在单继承中,super
也能够看作对其父类的指代,它的使用场合就是用来调用父类的方法:
__init__
方法它的写法是 super(Son,self).xxx
, 固然也能够写成 super()
这种简写的形式。
来看代码
class Father(): """ 这是一个父类 """ def __init__(self, age): print("Father's init function invoke") self.__age = age def get_age(self): return self.__age class Son(Father): """ 这是一个子类 """ def __init__(self, age): print("Son's init function invoke") self.__age = age super(Son, self).__init__(age + 25) def get_age(self): return self.__age def get_father_age(self): return super(Son, self).get_age() son = Son(5) # Son's init function invoke # Father's init function invoke print(son.get_father_age()) # 30 print(son.get_age()) # 5
经过代码来窥探下它的执行原理,以 super(Son, self).get_age()
为例
self
是 Son
的一个实例, super
把 self
转化为父类 Father
的一个实例对象self
通过了转化, 那它获得的 __age
, 也是父类初始化时获得的 __age
看到这里,不知您对 python 的面向对象有了多少理解,反正我是理解了很多,哈哈。若是有疑问和建议,欢迎留言交流,我将仔细阅读,认真回复。
下篇文章中会涉及到 文件, json xml 处理 处理等主题,敬请期待~