版權宣告:本套技術專欄是作者(秦凱新)平時工作的總結和昇華,通過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和叢集環境容量規劃等內容,請持續關注本套部落格。QQ郵箱地址:1120746959@qq.com,如有任何學術交流,可隨時聯絡。
1 python與scala物件導向對比分析
1.1 scala物件導向
-
定義類,包含field以及方法
class HelloWorld { private var name = "leo" def sayHello() { print("Hello, " + name) } def getName = name } 建立類的物件,並呼叫其方法 val helloWorld = new HelloWorld helloWorld.sayHello() print(helloWorld.getName()) 也可以不加括號,如果定義方法時不帶括號,則呼叫方法時也不能帶括號 print(helloWorld.getName) 複製程式碼
-
scala自動生成getter與setter
1 定義不帶private的var field,此時scala生成的面向JVM的類時, 會定義為private的name欄位,並提供public的getter和setter方法 2 而如果使用private修飾field,則生成的getter和setter也是private的 3 如果定義val field,則只會生成getter方法 4 如果不希望生成setter和getter方法,則將field宣告為private[this] class Student { var name = "leo" } 呼叫getter和setter方法,分別叫做name和name_ = val leo = new Student get方法 print(leo.name) set方法1 leo.name = "leo1" set方法2 scala> leo.name_=("leo2") scala> leo.name res3: String = leo2 複製程式碼
-
自定義getter與setter方法
如果只是希望擁有簡單的getter和setter方法,那麼就按照scala提供的語法規則,根據需求為field選擇合適的修飾符就好:var、val、private、private[this] 但是如果希望能夠自己對getter與setter進行控制,則可以自定義getter與setter方法 自定義setter方法的時候一定要注意scala的語法限制,簽名、=、引數間不能有空格 雖然設定私有private,單提供了公共的set和get方法,可以對外訪問 class Student { private var myName = "leo" def name = "your name is " + myName def name_=(newValue: String) { print("you cannot edit your name!!!") } } scala> val leo = new Student leo: Student = Student@e36bc01 scala> print(leo.name) your name is leo scala> leo.name = "leo1" you cannot edit your name!!!leo.name: String = your name is leo 一旦設定私有private,則對外將不可以訪問 class Student { private var myName = "leo" } scala> ss.name <console>:26: error: value name is not a member of Student ss.name ^ scala> ss.name_="asd" <console>:1: error: identifier expected but string literal found. ss.name_="asd" 複製程式碼
-
Java風格的getter和setter方法
Scala的getter和setter方法的命名與java是不同的,是field和field_=的方式 如果要讓scala自動生成java風格的getter和setter方法,只要給field新增@BeanProperty註解即可 此時會生成4個方法, name: String、 name_=(newValue: String): Unit getName():String、 setName(newValue: String): Unit import scala.reflect.BeanProperty class Student { @BeanProperty var name: String = _ } class Student(@BeanProperty var name: String) val s = new Student s.setName("leo") s.getName() 複製程式碼
-
輔助constructor
Scala中,可以給類定義多個輔助constructor,類似於java中的建構函式過載 輔助constructor之間可以互相呼叫,而且必須第一行呼叫主constructor class Student { private var name = "" private var age = 0 def this(name: String) { this() this.name = name } def this(name: String, age: Int) { this(name) this.age = age } } 複製程式碼
-
主constructor(除了方法體內的程式碼都會執行)
Scala中,主constructor是與類名放在一起的,與java不同 而且類中,沒有定義在任何方法或者是程式碼塊之中的程式碼,就是主constructor的程式碼,這點感覺沒有java那麼清晰 class Student(val name: String, val age: Int) { println("your name is " + name + ", your age is " + age) } 主constructor中還可以通過使用預設引數,來給引數預設的值 class Student(val name: String = "leo", val age: Int = 30) { println("your name is " + name + ", your age is " + age) } 如果主constrcutor傳入的引數什麼修飾都沒有,比如name: String,那麼如果類內部的方法使用到了,則會宣告為private[this] name;否則沒有該field,就只能被constructor程式碼使用而已 複製程式碼
-
伴生物件
1 如果有一個class,還有一個與class同名的object,那麼就稱這個object是class的伴生物件 ,class是object的伴生類 2 伴生類和伴生物件必須存放在一個.scala檔案之中 3 伴生類和伴生物件,最大的特點就在於,互相可以訪問private field object Person { private val eyeNum = 2 def getEyeNum = eyeNum } class Person(val name: String, val age: Int) { def sayHello = println("Hi, " + name + ", I guess you are " + age + " years old!" + ", and usually you must have " + Person.eyeNum + " eyes.") } scala> val s =new Person("leo",12) s: Person = Person@4d0abb23 scala> s.sayHello Hi, leo, I guess you are 12 years old!, and usually you must have 2 eyes. 複製程式碼
-
apply方法
1 object中非常重要的一個特殊方法,就是apply方法,在建立伴生類的物件時,通常不會使用new Class的方式,而是使用Class()的方式,隱式地呼叫伴生物件得apply方法,這樣會讓物件建立更加簡潔 比如,Array類的伴生物件的apply方法就實現了接收可變數量的引數,並建立一個Array物件的功能 val a = Array(1, 2, 3, 4, 5) 2 定義自己的伴生類和伴生物件,省略掉new操作 class Person(val name: String) object Person { def apply(name: String) = new Person(name) } scala> Person("xin") res7: Person = Person@484a5ddd 複製程式碼
-
main方法
scala中的main方法定義為def main(args: Array[String]),而且必須定義在object中 除了自己實現main方法之外,還可以繼承App Trait,然後將需要在main方法中執行的程式碼,直接作為object的constructor程式碼;而且用args可以接受傳入的引數 object HelloWorld extends App { if (args.length > 0) println("hello, " + args(0)) else println("Hello World!!!") } 複製程式碼
-
extends
子類可以覆蓋父類的field和method;但是如果父類用final修飾,field和method用final修 飾,則該類是無法被繼承的,field和method是無法被覆蓋的 class Person { private var name = "leo" def getName = name } class Student extends Person { private var score = "A" def getScore = score } 複製程式碼
-
override和super
Scala中,如果子類要覆蓋一個父類中的非抽象方法,則必須使用override關鍵字,在子類覆 蓋父類方法之後,如果我們在子類中就是要呼叫父類的被覆蓋的方法呢?那就可以使用super 關鍵字,顯式地指定要呼叫父類的方法 class Person { private var name = "leo" def getName = name } class Student extends Person { private var score = "A" def getScore = score override def getName = "Hi, I'm " + super.getName } 複製程式碼
-
類判別
class Person class Student extends Person scala> val p: Person = new Student p: Person = Student@683fac7e scala> p.isInstanceOf[Person] res10: Boolean = true scala> p.getClass == classOf[Person] res11: Boolean = false scala> p.getClass == classOf[Student] res12: Boolean = true 複製程式碼
-
使用模式匹配進行型別判斷
使用模式匹配,功能性上來說,與isInstanceOf一樣,也是判斷主要是該類以及該類的子類 的物件即可,不是精準判斷的 class Person class Student extends Person val p: Person = new Student scala> p match { | case per: Person => println("it's Person's object") | case _ => println("unknown type") | } it's Person's object 複製程式碼
-
呼叫父類的constructor
1 Scala中,每個類可以有一個主constructor和任意多個輔助constructor,而每個輔助constructor的第一行都必須是呼叫其他輔助constructor或者是主constructor;因此子類的輔助constructor是一定不可能直接呼叫父類的constructor的 2 只能在子類的主constructor中呼叫父類的constructor,以下這種語法,就是通過子類的主建構函式來呼叫父類的建構函式 3 注意!如果是父類中接收的引數,比如name和age,子類中接收時,就不要用任何val或var來修飾了,否則會認為是子類要覆蓋父類的field class Person(val name: String, val age: Int) class Student(name: String, age: Int, var score: Double) extends Person(name, age) { def this(name: String) { this(name, 0, 0) } def this(age: Int) { this("leo", age, 0) } } 複製程式碼
-
將trait作為介面使用
Scala中的Triat是一種特殊的概念 首先我們可以將Trait作為介面來使用,此時的Triat就與Java中的介面非常類似 在triat中可以定義抽象方法,就與抽象類中的抽象方法一樣,只要不給出方法的具體實現即可 類可以使用extends關鍵字繼承trait,注意,這裡不是implement,而是extends,在scala中沒有implement的概念,無論繼承類還是trait,統一都是extends 類繼承trait後,必須實現其中的抽象方法,實現時不需要使用override關鍵字 scala不支援對類進行多繼承,但是支援多重繼承trait,使用with關鍵字即可 trait HelloTrait { def sayHello(name: String) } trait MakeFriendsTrait { def makeFriends(p: Person) } class Person(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable { def sayHello(name: String) = println("Hello, " + name) def makeFriends(p: Person) = println("Hello, my name is " + name + ", your name is " + p.name) } 複製程式碼
-
在Trait中定義具體欄位
Scala中的Triat可以定義具體field,此時繼承trait的類就自動獲得了trait中定義的field 但是這種獲取field的方式與繼承class是不同的:如果是繼承class獲取的field,實際是定 義在父類中的;而繼承trait獲取的field,就直接被新增到了類中。 trait Person { val eyeNum: Int = 2 } class Student(val name: String) extends Person { def sayHello = println("Hi, I'm " + name + ", I have " + eyeNum + " eyes.") } 複製程式碼
-
在Trait中定義抽象欄位
// Scala中的Triat可以定義抽象field,而trait中的具體方法則可以基於抽象field來編寫 // 但是繼承trait的類,則必須覆蓋抽象field,提供具體的值 trait SayHello { val msg: String def sayHello(name: String) = println(msg + ", " + name) } class Person(val name: String) extends SayHello { val msg: String = "hello" def makeFriends(p: Person) { sayHello(p.name) println("I'm " + name + ", I want to make friends with you!") } } 複製程式碼
-
為例項混入trait(如果不使用With,trait方法不會執行)
trait Logged { def log(msg: String) {} } trait MyLogger extends Logged { override def log(msg: String) { println("log: " + msg) } } class Person(val name: String) extends Logged { def sayHello { println("Hi, I'm " + name); log("sayHello is invoked!") } } val p1 = new Person("leo") p1.sayHello val p2 = new Person("jack") with MyLogger p2.sayHello scala> val p1 = new Person("leo") p1: Person = Person@75b3ef1a scala> p1.sayHello Hi, I'm leo scala> val p2 = new Person("jack") with MyLogger p2: Person with MyLogger = $anon$1@703eead0 scala> p2.sayHello Hi, I'm jack log: sayHello is invoked! 複製程式碼
-
trait呼叫鏈
Scala中支援讓類繼承多個trait後,依次呼叫多個trait中的同一個方法,只要讓多個trait的同一個方法中,在最後都執行super.方法即可。 類中呼叫多個trait中都有的這個方法時,首先會從最右邊的trait的方法開始執行,然後依次往左執行,形成一個呼叫鏈條 這種特性非常強大,其實就相當於設計模式中的責任鏈模式的一種具體實現依賴 trait Handler { def handle(data: String) {} } trait DataValidHandler2 extends Handler { override def handle(data: String) { println("check data2: " + data) super.handle(data) } } trait DataValidHandler1 extends Handler { override def handle(data: String) { println("check data1: " + data) super.handle(data) } } trait SignatureValidHandler extends Handler { override def handle(data: String) { println("check signature: " + data) super.handle(data) } } class Person(val name: String) extends SignatureValidHandler with DataValidHandler1 with DataValidHandler2 { def sayHello = { println("Hello, " + name); handle(name) } } scala> val p = new Person("person") p: Person = Person@7a85454b scala> p.sayHello Hello, person check data2: person check data1: person check signature: person 複製程式碼
-
在trait中覆蓋抽象方法
在trait中,是可以覆蓋父trait的抽象方法的 但是覆蓋時,如果使用了super.方法的程式碼,則無法通過編譯。因為super.方法就會去掉用父trait的抽象方法,此時子trait的該方法還是會被認為是抽象的 此時如果要通過編譯,就得給子trait的方法加上abstract override修飾 trait Logger { def log(msg: String) } trait MyLogger extends Logger { abstract override def log(msg: String) { super.log(msg) } } 複製程式碼
-
trait的構造機制(從左到右執行)
class Person { println("Person's constructor!") } trait Logger { println("Logger's constructor!") } trait MyLogger extends Logger { println("MyLogger's constructor!") } trait TimeLogger extends Logger { println("TimeLogger's constructor!") } class Student extends Person with MyLogger with TimeLogger { println("Student's constructor!") } scala> val s = new Student Person's constructor! Logger's constructor! MyLogger's constructor! TimeLogger's constructor! Student's constructor! s: Student = Student@34a99d8 複製程式碼
1.2 python物件導向
-
定義類並建立例項
按照 Python 的程式設計習慣,類名以大寫字母開頭,緊接著是(object),表示該類是從哪個類繼承下來的。 class Person(object): pass 省掉new建立例項 xiaoming = Person() xiaohong = Person() 複製程式碼
-
由於Python是動態語言,對每一個例項,都可以直接給他們的屬性賦值,臨時追加屬性
xiaoming = Person() xiaoming.name = 'Xiao Ming' xiaoming.gender = 'Male' xiaoming.birth = '1990-1-1' print(xiaoming.name) Xiao Ming xiaohong.grade = xiaohong.grade + 1 複製程式碼
-
初始化例項屬性
init() 方法的第一個引數必須是self(也可以用別的名字,但建議使用習慣用法), 後續引數則可以自由指定,和定義函式沒有任何區別,相應地,建立例項時,就必須要提供除 self 以外的引數 class Person(object): def __init__(self, name, gender, birth): self.name = name self.gender = gender self.birth = birth xiaoming = Person('Xiao Ming', 'Male', '1991-1-1') xiaohong = Person('Xiao Hong', 'Female', '1992-2-2') print (xiaoming.name) Xiao Ming 複製程式碼
-
例項屬性訪問限制
Python對屬性許可權的控制是通過屬性名來實現的,如果一個屬性由雙下劃線開頭(__), 該屬性就無法被外部訪問 class Person(object): def __init__(self, name): self.name = name self._title = 'Mr' self.__job = 'Student' p = Person('Bob') print p.name # => Bob print(p._title) # => Mr print p.__job --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-28-13a5b5479af8> in <module>() ----> 1 print(p.__job) AttributeError: 'Person' object has no attribute '__job' 複製程式碼
-
建立類屬性
類本身也是一個物件,如果在類上繫結一個屬性,則所有例項都可以訪問類的屬性 class Person(object): address = 'Earth' def __init__(self, name): self.name = name print (Person.address) Earth p1 = Person('Bob') p2 = Person('Alice') print p1.address # => Earth print p2.address # => Earth 由於Python是動態語言,類屬性也是可以動態新增和修改的 Person.address = 'China' print p1.address # => 'China' print p2.address # => 'China' 類屬性和例項實型名字衝突怎麼辦?當例項屬性和類屬性重名時,例項屬性優先順序高,它將遮蔽掉對類屬性的訪問。 class Person(object): address = 'Earth' def __init__(self, name): self.name = name p1 = Person('Bob') p2 = Person('Alice') print 'Person.address = ' + Person.address p1.address = 'China' print ('p1.address = ' + p1.address) p1.address = China print ('p2.address = ' + p2.address) p2.address = Earth 複製程式碼
-
定義例項方法
一個例項的私有屬性就是以__開頭的屬性,無法被外部訪問,例項的方法就是在類中定義的 函式,它的第一個引數永遠是 self,雖然name是私有的不能被訪問, 但是get_name就可以被訪問。 class Person(object): def __init__(self, name): self.__name = name def get_name(self): return self.__name p1 = Person('Bob') print(p1.get_name()) Bob 複製程式碼
-
把方法追加到類上
import types def fn_get_grade(self): if self.score >= 80: return 'A' if self.score >= 60: return 'B' return 'C' class Person(object): def __init__(self, name, score): self.name = name self.score = score p1 = Person('Bob', 90) p1.get_grade = types.MethodType(fn_get_grade, p1) print (p1.get_grade()) A 複製程式碼
-
定義類方法
class Person(object): count = 0 @classmethod def how_many(cls): return cls.count def __init__(self, name): self.name = name Person.count = Person.count + 1 print(Person.how_many()) 0 p1 = Person('Bob') print(Person.how_many()) 3 複製程式碼
-
繼承一個類
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(s, Student) True isinstance(p , Person) True isinstance(t , Student) False 複製程式碼
-
特殊方法
1 str和repr class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender def __str__(self): return '(Person: %s, %s)' % (self.name, self.gender) p = Person('Bob', 'male') print(p) (Person: Bob, male) 2 Python的 sorted() 按照預設的比較函式 cmp 排序 class Student(object): def __init__(self, name, score): self.name = name self.score = score def __str__(self): return '(%s: %s)' % (self.name, self.score) __repr__ = __str__ def __cmp__(self, s): if self.name < s.name: return -1 elif self.name > s.name: return 1 else: return 0 L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77)] print(L) [(Tim: 99), (Bob: 88), (Alice: 77)] 3 len class Students(object): def __init__(self, *args): self.names = args def __len__(self): return len(self.names) ss = Students('Bob', 'Alice', 'Tim') print (len(ss)) 3 複製程式碼
-
在Python中,函式其實是一個物件
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender def __call__(self, friend): print ('My name is %s...' % self.name) print ('My friend is %s...' % friend) p = Person('Bob', 'male') p('Tim') My name is Bob... My friend is Tim... 複製程式碼
2 總結
通過Python技術棧與Spark大資料資料平臺整合,必然需要本文進行詳細對比,粗陋成文,在於作者複習,勿怪。
秦凱新 於深圳 201812132319
版權宣告:本套技術專欄是作者(秦凱新)平時工作的總結和昇華,通過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和叢集環境容量規劃等內容,請持續關注本套部落格。QQ郵箱地址:1120746959@qq.com,如有任何學術交流,可隨時聯絡。