The problem is that the object that receive the signal (your Worker class) lives in another thread.
From the Qt docs:
QObject * QObject::sender () const [protected] Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns 0. The pointer is valid only during the execution of the slot that calls this function from this object's thread context.
The pointer returned by this function becomes invalid if the sender is destroyed, or if the slot is disconnected from the sender's signal.
Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.
Warning: As mentioned above, the return value of this function is not valid when the slot is called via a Qt::DirectConnection from a thread different from this object's thread. Do not use this function in this type of scenario.
If you don't move the object to the other thread, it works (the examples is in python3, but it will work in python 2, after changing the print lines):
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Worker(QObject):
def init(self):
print("worker is ready.")
@Slot()
def work(self):
derp = self.sender()
print ("i am tired, {}".format(derp))
derp.setText("It works!")
app = QApplication(sys.argv)
button = QPushButton("Kick!")
button.show()
worker = Worker()
button.clicked.connect(worker.work)
app.exec_()
sys.exit()